Questa pagina contiene snippet di codice e descrizioni delle funzioni disponibili per personalizzare un'app Android TV Ricevitore.
Configurazione delle librerie
Per rendere le API Cast Connect disponibili per la tua app Android TV:
-
Apri il file
build.gradle
nella directory del modulo dell'applicazione. -
Verifica che
google()
sia incluso neirepositories
elencati.repositories { google() }
-
Aggiungi le versioni più recenti a seconda del tipo di dispositivo target della tua app
delle librerie alle tue dipendenze:
-
Per l'app Android Ricevitore:
dependencies { implementation 'com.google.android.gms:play-services-cast-tv:21.1.0' implementation 'com.google.android.gms:play-services-cast:21.5.0' }
-
Per l'app Android Sender:
dependencies { implementation 'com.google.android.gms:play-services-cast:21.1.0' implementation 'com.google.android.gms:play-services-cast-framework:21.5.0' }
-
Per l'app Android Ricevitore:
-
Salva le modifiche e fai clic su
Sync Project with Gradle Files
nella barra degli strumenti.
-
Assicurati che
Podfile
abbia come targetgoogle-cast-sdk
4.8.1 o successiva -
Scegli come target iOS 14 o versioni successive. Vedi le note di rilascio
per ulteriori informazioni.
platform: ios, '14' def target_pods pod 'google-cast-sdk', '~>4.8.1' end
- Richiede il browser Chromium M87 o versioni successive.
-
Aggiungi la libreria dell'API Web Sender al progetto
<script src="//www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
Requisito AndroidX
Per poter utilizzare le nuove versioni di Google Play Services è necessario aggiornare un'app
lo spazio dei nomi androidx
. Segui le istruzioni per
eseguire la migrazione ad AndroidX.
App per Android TV: prerequisiti
Per supportare Cast Connect nella tua app Android TV, devi creare e eventi di supporto di una sessione multimediale. I dati forniti dalla tua sessione multimediale fornisce le informazioni di base, ad esempio posizione, stato di riproduzione e così via, per lo stato dei contenuti multimediali. La sessione multimediale viene utilizzata anche dalla raccolta Cast Connect per segnalare quando ha ricevuto determinati messaggi da un mittente, ad esempio "metti in pausa".
Per ulteriori informazioni su una sessione multimediale e su come inizializzarla, vedi il lavorare con una guida per le sessioni multimediali.
Ciclo di vita della sessione multimediale
L'app dovrebbe creare una sessione multimediale all'avvio della riproduzione e rilasciarla quando non può più essere controllato. Ad esempio, se la tua app è un'app video, deve rilasciare la sessione quando l'utente esce dall'attività di riproduzione, tramite selezionando "Indietro" per sfogliare altri contenuti o mettere l'app in background. Se le tue è un'app di musica, dovresti rilasciarla quando l'app non sta più riproducendo contenuti multimediali.
Aggiornamento stato sessione
I dati della sessione multimediale devono essere sempre aggiornati con lo stato del tuo un player. Ad esempio, quando la riproduzione è in pausa, devi aggiornare la riproduzione così come le azioni supportate. Le tabelle seguenti elencano gli stati che è tua responsabilità tenerti al corrente.
MediaMetadataCompat
Campo metadati | Descrizione |
---|---|
METADATA_KEY_TITLE (obbligatorio) | Il titolo dell'elemento multimediale. |
METADATA_KEY_DISPLAY_SUBTITLE | Il sottotitolo. |
METADATA_KEY_DISPLAY_ICON_URI | L'URL dell'icona. |
METADATA_KEY_DURATION (obbligatorio) | Durata dei contenuti multimediali. |
METADATA_KEY_MEDIA_URI | Content ID. |
METADATA_KEY_ARTIST | L'artista. |
METADATA_KEY_ALBUM | L'album. |
PlaybackStateCompat
Metodo richiesto | Descrizione |
---|---|
setActions() | Consente di impostare i comandi multimediali supportati. |
setState() | Imposta lo stato di riproduzione e la posizione corrente. |
MediaSessionCompat
Metodo richiesto | Descrizione |
---|---|
setRepeatMode() | Imposta la modalità di ripetizione. |
setShuffleMode() | Consente di impostare la modalità casuale. |
setMetadata() | Imposta i metadati multimediali. |
setPlaybackState() | Imposta lo stato di riproduzione. |
private fun updateMediaSession() { val metadata = MediaMetadataCompat.Builder() .putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title") .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "subtitle") .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, mMovie.getCardImageUrl()) .build() val playbackState = PlaybackStateCompat.Builder() .setState( PlaybackStateCompat.STATE_PLAYING, player.getPosition(), player.getPlaybackSpeed(), System.currentTimeMillis() ) .build() mediaSession.setMetadata(metadata) mediaSession.setPlaybackState(playbackState) }
private void updateMediaSession() { MediaMetadataCompat metadata = new MediaMetadataCompat.Builder() .putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title") .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "subtitle") .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI,mMovie.getCardImageUrl()) .build(); PlaybackStateCompat playbackState = new PlaybackStateCompat.Builder() .setState( PlaybackStateCompat.STATE_PLAYING, player.getPosition(), player.getPlaybackSpeed(), System.currentTimeMillis()) .build(); mediaSession.setMetadata(metadata); mediaSession.setPlaybackState(playbackState); }
Gestione del controllo del trasporto
L'app deve implementare il callback di controllo del trasporto delle sessioni multimediali. La la seguente tabella mostra quali azioni di controllo del trasporto devono gestire:
MediaSessionCompat.Callback
Azioni | Descrizione |
---|---|
onPlay() | Riprendi |
onPause() | Metti in pausa |
onSeekTo() | Vai a una posizione |
onStop() | Interrompere i contenuti multimediali correnti |
class MyMediaSessionCallback : MediaSessionCompat.Callback() { override fun onPause() { // Pause the player and update the play state. ... } override fun onPlay() { // Resume the player and update the play state. ... } override fun onSeekTo (long pos) { // Seek and update the play state. ... } ... } mediaSession.setCallback( MyMediaSessionCallback() );
public MyMediaSessionCallback extends MediaSessionCompat.Callback { public void onPause() { // Pause the player and update the play state. ... } public void onPlay() { // Resume the player and update the play state. ... } public void onSeekTo (long pos) { // Seek and update the play state. ... } ... } mediaSession.setCallback(new MyMediaSessionCallback());
Configurazione del supporto di Cast
Quando una richiesta di avvio viene inviata da un'applicazione mittente, viene creato un intent.
con uno spazio dei nomi dell'applicazione. La tua applicazione è responsabile della sua gestione
e la creazione di un'istanza
CastReceiverContext
all'avvio dell'app TV. L'oggetto CastReceiverContext
è necessario
per interagire con Google Cast mentre è in esecuzione l'app TV. Questo oggetto abilita la TV
per accettare messaggi multimediali di trasmissione provenienti da mittenti connessi.
Configurazione di Android TV
Aggiunta di un filtro per intent di avvio
Aggiungi un nuovo filtro per intent all'attività che vuoi gestire il lancio intent dall'app del mittente:
<activity android:name="com.example.activity">
<intent-filter>
<action android:name="com.google.android.gms.cast.tv.action.LAUNCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Specifica il provider opzioni ricevitore
Devi implementare un
ReceiverOptionsProvider
per fornire
CastReceiverOptions
:
class MyReceiverOptionsProvider : ReceiverOptionsProvider { override fun getOptions(context: Context?): CastReceiverOptions { return CastReceiverOptions.Builder(context) .setStatusText("My App") .build() } }
public class MyReceiverOptionsProvider implements ReceiverOptionsProvider { @Override public CastReceiverOptions getOptions(Context context) { return new CastReceiverOptions.Builder(context) .setStatusText("My App") .build(); } }
Quindi, specifica il fornitore di opzioni nel tuo AndroidManifest
:
<meta-data
android:name="com.google.android.gms.cast.tv.RECEIVER_OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.example.mysimpleatvapplication.MyReceiverOptionsProvider" />
ReceiverOptionsProvider
viene utilizzato per fornire CastReceiverOptions
quando
CastReceiverContext
è stato inizializzato.
Contesto ricevitore di trasmissione
Inizializza
CastReceiverContext
quando viene creata l'app:
override fun onCreate() { CastReceiverContext.initInstance(this) ... }
@Override public void onCreate() { CastReceiverContext.initInstance(this); ... }
Avvia CastReceiverContext
quando la tua app passa in primo piano:
CastReceiverContext.getInstance().start()
CastReceiverContext.getInstance().start();
Chiama
stop()
il
CastReceiverContext
Dopo che l'app è passata in background per le app video o le app che non supportano
Riproduzione in background:
// Player has stopped. CastReceiverContext.getInstance().stop()
// Player has stopped. CastReceiverContext.getInstance().stop();
Inoltre, se la tua app supporta la riproduzione in background, chiama stop()
su CastReceiverContext
quando interrompe la riproduzione in background.
Ti consigliamo vivamente di utilizzare LifecycleObserver dal
androidx.lifecycle
libreria per gestire le chiamate
CastReceiverContext.start()
e
CastReceiverContext.stop()
,
soprattutto se la tua app nativa
ha più attività. Questo evita il gruppo etnico
condizioni quando chiami start()
e stop()
da attività diverse.
// Create a LifecycleObserver class. class MyLifecycleObserver : DefaultLifecycleObserver { override fun onStart(owner: LifecycleOwner) { // App prepares to enter foreground. CastReceiverContext.getInstance().start() } override fun onStop(owner: LifecycleOwner) { // App has moved to the background or has terminated. CastReceiverContext.getInstance().stop() } } // Add the observer when your application is being created. class MyApplication : Application() { fun onCreate() { super.onCreate() // Initialize CastReceiverContext. CastReceiverContext.initInstance(this /* android.content.Context */) // Register LifecycleObserver ProcessLifecycleOwner.get().lifecycle.addObserver( MyLifecycleObserver()) } }
// Create a LifecycleObserver class. public class MyLifecycleObserver implements DefaultLifecycleObserver { @Override public void onStart(LifecycleOwner owner) { // App prepares to enter foreground. CastReceiverContext.getInstance().start(); } @Override public void onStop(LifecycleOwner owner) { // App has moved to the background or has terminated. CastReceiverContext.getInstance().stop(); } } // Add the observer when your application is being created. public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); // Initialize CastReceiverContext. CastReceiverContext.initInstance(this /* android.content.Context */); // Register LifecycleObserver ProcessLifecycleOwner.get().getLifecycle().addObserver( new MyLifecycleObserver()); } }
// In AndroidManifest.xml set MyApplication as the application class
<application
...
android:name=".MyApplication">
Connessione di MediaSession a MediaManager
Quando crei un
MediaSession
,
devi fornire anche il token MediaSession
corrente
CastReceiverContext
in modo che sappia dove inviare i comandi e recuperare lo stato di riproduzione dei contenuti multimediali:
val mediaManager: MediaManager = receiverContext.getMediaManager() mediaManager.setSessionCompatToken(currentMediaSession.getSessionToken())
MediaManager mediaManager = receiverContext.getMediaManager(); mediaManager.setSessionCompatToken(currentMediaSession.getSessionToken());
Quando rilasci MediaSession
per riproduzione inattiva, devi impostare un
token nullo
MediaManager
:
myPlayer.stop() mediaSession.release() mediaManager.setSessionCompatToken(null)
myPlayer.stop(); mediaSession.release(); mediaManager.setSessionCompatToken(null);
Se la tua app supporta la riproduzione di contenuti multimediali mentre è in background,
delle chiamate
CastReceiverContext.stop()
quando la tua app viene inviata in background, dovresti chiamarla soltanto quando la tua app
è in background e non riproduce più contenuti multimediali. Ad esempio:
class MyLifecycleObserver : DefaultLifecycleObserver { ... // App has moved to the background. override fun onPause(owner: LifecycleOwner) { mIsBackground = true myStopCastReceiverContextIfNeeded() } } // Stop playback on the player. private fun myStopPlayback() { myPlayer.stop() myStopCastReceiverContextIfNeeded() } // Stop the CastReceiverContext when both the player has // stopped and the app has moved to the background. private fun myStopCastReceiverContextIfNeeded() { if (mIsBackground && myPlayer.isStopped()) { CastReceiverContext.getInstance().stop() } }
public class MyLifecycleObserver implements DefaultLifecycleObserver { ... // App has moved to the background. @Override public void onPause(LifecycleOwner owner) { mIsBackground = true; myStopCastReceiverContextIfNeeded(); } } // Stop playback on the player. private void myStopPlayback() { myPlayer.stop(); myStopCastReceiverContextIfNeeded(); } // Stop the CastReceiverContext when both the player has // stopped and the app has moved to the background. private void myStopCastReceiverContextIfNeeded() { if (mIsBackground && myPlayer.isStopped()) { CastReceiverContext.getInstance().stop(); } }
Utilizzare Exoplayer con Cast Connect
Se utilizzi
Exoplayer
, puoi utilizzare
MediaSessionConnector
per mantenere automaticamente la sessione e tutte le informazioni correlate, tra cui
lo stato di riproduzione anziché monitorare le modifiche manualmente.
MediaSessionConnector.MediaButtonEventHandler
può essere utilizzato per gestire eventi MediaButton chiamando
setMediaButtonEventHandler(MediaButtonEventHandler)
che vengono altrimenti gestiti
MediaSessionCompat.Callback
per impostazione predefinita.
Per l'integrazione
MediaSessionConnector
nella tua app, aggiungi quanto segue alla tua classe attività giocatore o dovunque
gestire la sessione multimediale:
class PlayerActivity : Activity() { private var mMediaSession: MediaSessionCompat? = null private var mMediaSessionConnector: MediaSessionConnector? = null private var mMediaManager: MediaManager? = null override fun onCreate(savedInstanceState: Bundle?) { ... mMediaSession = MediaSessionCompat(this, LOG_TAG) mMediaSessionConnector = MediaSessionConnector(mMediaSession!!) ... } override fun onStart() { ... mMediaManager = receiverContext.getMediaManager() mMediaManager!!.setSessionCompatToken(currentMediaSession.getSessionToken()) mMediaSessionConnector!!.setPlayer(mExoPlayer) mMediaSessionConnector!!.setMediaMetadataProvider(mMediaMetadataProvider) mMediaSession!!.isActive = true ... } override fun onStop() { ... mMediaSessionConnector!!.setPlayer(null) mMediaSession!!.release() mMediaManager!!.setSessionCompatToken(null) ... } }
public class PlayerActivity extends Activity { private MediaSessionCompat mMediaSession; private MediaSessionConnector mMediaSessionConnector; private MediaManager mMediaManager; @Override protected void onCreate(Bundle savedInstanceState) { ... mMediaSession = new MediaSessionCompat(this, LOG_TAG); mMediaSessionConnector = new MediaSessionConnector(mMediaSession); ... } @Override protected void onStart() { ... mMediaManager = receiverContext.getMediaManager(); mMediaManager.setSessionCompatToken(currentMediaSession.getSessionToken()); mMediaSessionConnector.setPlayer(mExoPlayer); mMediaSessionConnector.setMediaMetadataProvider(mMediaMetadataProvider); mMediaSession.setActive(true); ... } @Override protected void onStop() { ... mMediaSessionConnector.setPlayer(null); mMediaSession.release(); mMediaManager.setSessionCompatToken(null); ... } }
Configurazione dell'app mittente
Attiva il supporto di Cast Connect
Dopo aver aggiornato l'app del mittente con il supporto di Cast Connect, puoi dichiarare
per l'idoneità impostando il parametro
androidReceiverCompatible
segnala su
LaunchOptions
su true.
Richiede la versione play-services-cast-framework
19.0.0
o superiore.
Il flag androidReceiverCompatible
è impostato in
LaunchOptions
(che fa parte di CastOptions
):
class CastOptionsProvider : OptionsProvider { override fun getCastOptions(context: Context?): CastOptions { val launchOptions: LaunchOptions = Builder() .setAndroidReceiverCompatible(true) .build() return CastOptions.Builder() .setLaunchOptions(launchOptions) ... .build() } }
public class CastOptionsProvider implements OptionsProvider { @Override public CastOptions getCastOptions(Context context) { LaunchOptions launchOptions = new LaunchOptions.Builder() .setAndroidReceiverCompatible(true) .build(); return new CastOptions.Builder() .setLaunchOptions(launchOptions) ... .build(); } }
Richiede google-cast-sdk
versione v4.4.8
o
in alto.
Il flag androidReceiverCompatible
è impostato in
GCKLaunchOptions
(che fa parte di
GCKCastOptions
):
let options = GCKCastOptions(discoveryCriteria: GCKDiscoveryCriteria(applicationID: kReceiverAppID)) ... let launchOptions = GCKLaunchOptions() launchOptions.androidReceiverCompatible = true options.launchOptions = launchOptions GCKCastContext.setSharedInstanceWith(options)
È necessaria la versione del browser Chromium
M87
o superiore.
const context = cast.framework.CastContext.getInstance(); const castOptions = new cast.framework.CastOptions(); castOptions.receiverApplicationId = kReceiverAppID; castOptions.androidReceiverCompatible = true; context.setOptions(castOptions);
Configurazione di Cast Console per gli sviluppatori
Configurare l'app Android TV
Aggiungi il nome del pacchetto della tua app Android TV in Console per gli sviluppatori di Google Cast per associarlo all'ID dell'app Google Cast.
Registra dispositivi per sviluppatori
Registra il numero di serie del dispositivo Android TV che intendi utilizzare. per lo sviluppo Console per gli sviluppatori di Google Cast.
Senza registrazione, Cast Connect funziona solo per le app installate da Google Play Store per motivi di sicurezza.
Per ulteriori informazioni sulla registrazione di un dispositivo Cast o Android TV per la trasmissione di sviluppo, consulta la pagina di registrazione.
Caricamento dei contenuti multimediali in corso...
Se hai già implementato il supporto dei link diretti nella tua app per Android TV, Dovresti avere una definizione simile configurata nel tuo file manifest di Android TV:
<activity android:name="com.example.activity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https"/>
<data android:host="www.example.com"/>
<data android:pathPattern=".*"/>
</intent-filter>
</activity>
Carica per entità sul mittente
Sui mittenti, puoi trasmettere il link diretto impostando il entity
nei contenuti multimediali
informazioni per la richiesta di caricamento:
val mediaToLoad = MediaInfo.Builder("some-id") .setEntity("https://example.com/watch/some-id") ... .build() val loadRequest = MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") ... .build() remoteMediaClient.load(loadRequest)
MediaInfo mediaToLoad = new MediaInfo.Builder("some-id") .setEntity("https://example.com/watch/some-id") ... .build(); MediaLoadRequestData loadRequest = new MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") ... .build(); remoteMediaClient.load(loadRequest);
let mediaInfoBuilder = GCKMediaInformationBuilder(entity: "https://example.com/watch/some-id") ... mediaInformation = mediaInfoBuilder.build() let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder() mediaLoadRequestDataBuilder.mediaInformation = mediaInformation mediaLoadRequestDataBuilder.credentials = "user-credentials" ... let mediaLoadRequestData = mediaLoadRequestDataBuilder.build() remoteMediaClient?.loadMedia(with: mediaLoadRequestData)
È necessaria la versione del browser Chromium
M87
o superiore.
let mediaInfo = new chrome.cast.media.MediaInfo('some-id"', 'video/mp4'); mediaInfo.entity = 'https://example.com/watch/some-id'; ... let request = new chrome.cast.media.LoadRequest(mediaInfo); request.credentials = 'user-credentials'; ... cast.framework.CastContext.getInstance().getCurrentSession().loadMedia(request);
Il comando di caricamento viene inviato tramite un intent con il tuo link diretto e il nome del pacchetto che hai definito nella Developer Console.
Impostazione delle credenziali ATV sul mittente
È possibile che l'app WebRicevitore e l'app Android TV supportino diverse
Link diretti e credentials
(ad esempio se gestisci l'autenticazione
in modo diverso sulle due piattaforme). Per risolvere questo problema, puoi fornire alternative
entity
e credentials
per Android TV:
val mediaToLoad = MediaInfo.Builder("some-id") .setEntity("https://example.com/watch/some-id") .setAtvEntity("myscheme://example.com/atv/some-id") ... .build() val loadRequest = MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") .setAtvCredentials("atv-user-credentials") ... .build() remoteMediaClient.load(loadRequest)
MediaInfo mediaToLoad = new MediaInfo.Builder("some-id") .setEntity("https://example.com/watch/some-id") .setAtvEntity("myscheme://example.com/atv/some-id") ... .build(); MediaLoadRequestData loadRequest = new MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") .setAtvCredentials("atv-user-credentials") ... .build(); remoteMediaClient.load(loadRequest);
let mediaInfoBuilder = GCKMediaInformationBuilder(entity: "https://example.com/watch/some-id") mediaInfoBuilder.atvEntity = "myscheme://example.com/atv/some-id" ... mediaInformation = mediaInfoBuilder.build() let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder() mediaLoadRequestDataBuilder.mediaInformation = mediaInformation mediaLoadRequestDataBuilder.credentials = "user-credentials" mediaLoadRequestDataBuilder.atvCredentials = "atv-user-credentials" ... let mediaLoadRequestData = mediaLoadRequestDataBuilder.build() remoteMediaClient?.loadMedia(with: mediaLoadRequestData)
È necessaria la versione del browser Chromium
M87
o superiore.
let mediaInfo = new chrome.cast.media.MediaInfo('some-id"', 'video/mp4'); mediaInfo.entity = 'https://example.com/watch/some-id'; mediaInfo.atvEntity = 'myscheme://example.com/atv/some-id'; ... let request = new chrome.cast.media.LoadRequest(mediaInfo); request.credentials = 'user-credentials'; request.atvCredentials = 'atv-user-credentials'; ... cast.framework.CastContext.getInstance().getCurrentSession().loadMedia(request);
Se l'app Web Ricevitore viene avviata, utilizza entity
e credentials
in
la richiesta di caricamento. Tuttavia, se la tua app per Android TV viene avviata, l'SDK sostituisce
entity
e credentials
con atvEntity
e atvCredentials
(se specificato).
Caricamento tramite Content ID o MediaQueueData
Se non utilizzi entity
o atvEntity
e usi Content ID o
URL dei contenuti nelle informazioni sui contenuti multimediali oppure utilizza il campo Caricamento multimediale più dettagliato
Richiedi dati, devi aggiungere il seguente filtro per intent predefinito in
l'app Android TV:
<activity android:name="com.example.activity">
<intent-filter>
<action android:name="com.google.android.gms.cast.tv.action.LOAD"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Dal lato mittente, come per Carica per entità,
può creare una richiesta di caricamento con le informazioni sui tuoi contenuti e chiamare load()
.
val mediaToLoad = MediaInfo.Builder("some-id").build() val loadRequest = MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") ... .build() remoteMediaClient.load(loadRequest)
MediaInfo mediaToLoad = new MediaInfo.Builder("some-id").build(); MediaLoadRequestData loadRequest = new MediaLoadRequestData.Builder() .setMediaInfo(mediaToLoad) .setCredentials("user-credentials") ... .build(); remoteMediaClient.load(loadRequest);
let mediaInfoBuilder = GCKMediaInformationBuilder(contentId: "some-id") ... mediaInformation = mediaInfoBuilder.build() let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder() mediaLoadRequestDataBuilder.mediaInformation = mediaInformation mediaLoadRequestDataBuilder.credentials = "user-credentials" ... let mediaLoadRequestData = mediaLoadRequestDataBuilder.build() remoteMediaClient?.loadMedia(with: mediaLoadRequestData)
È necessaria la versione del browser Chromium
M87
o superiore.
let mediaInfo = new chrome.cast.media.MediaInfo('some-id"', 'video/mp4'); ... let request = new chrome.cast.media.LoadRequest(mediaInfo); ... cast.framework.CastContext.getInstance().getCurrentSession().loadMedia(request);
Gestione delle richieste di carico
Nella tua attività, per gestire queste richieste di caricamento, devi gestire gli intent nei callback del ciclo di vita delle attività:
class MyActivity : Activity() { override fun onStart() { super.onStart() val mediaManager = CastReceiverContext.getInstance().getMediaManager() // Pass the intent to the SDK. You can also do this in onCreate(). if (mediaManager.onNewIntent(intent)) { // If the SDK recognizes the intent, you should early return. return } // If the SDK doesn't recognize the intent, you can handle the intent with // your own logic. ... } // For some cases, a new load intent triggers onNewIntent() instead of // onStart(). override fun onNewIntent(intent: Intent) { val mediaManager = CastReceiverContext.getInstance().getMediaManager() // Pass the intent to the SDK. You can also do this in onCreate(). if (mediaManager.onNewIntent(intent)) { // If the SDK recognizes the intent, you should early return. return } // If the SDK doesn't recognize the intent, you can handle the intent with // your own logic. ... } }
public class MyActivity extends Activity { @Override protected void onStart() { super.onStart(); MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); // Pass the intent to the SDK. You can also do this in onCreate(). if (mediaManager.onNewIntent(getIntent())) { // If the SDK recognizes the intent, you should early return. return; } // If the SDK doesn't recognize the intent, you can handle the intent with // your own logic. ... } // For some cases, a new load intent triggers onNewIntent() instead of // onStart(). @Override protected void onNewIntent(Intent intent) { MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); // Pass the intent to the SDK. You can also do this in onCreate(). if (mediaManager.onNewIntent(intent)) { // If the SDK recognizes the intent, you should early return. return; } // If the SDK doesn't recognize the intent, you can handle the intent with // your own logic. ... } }
Se MediaManager
rileva che l'intent è un intent di caricamento, estrae un
MediaLoadRequestData
oggetto dall'intent e richiamare
MediaLoadCommandCallback.onLoad()
Devi eseguire l'override di questo metodo per gestire la richiesta di caricamento. Il callback deve
la registrazione prima del giorno
MediaManager.onNewIntent()
(è consigliabile utilizzare un'attività o un'applicazione onCreate()
).
class MyActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val mediaManager = CastReceiverContext.getInstance().getMediaManager() mediaManager.setMediaLoadCommandCallback(MyMediaLoadCommandCallback()) } } class MyMediaLoadCommandCallback : MediaLoadCommandCallback() { override fun onLoad( senderId: String?, loadRequestData: MediaLoadRequestData ): Task{ return Tasks.call { // Resolve the entity into your data structure and load media. val mediaInfo = loadRequestData.getMediaInfo() if (!checkMediaInfoSupported(mediaInfo)) { // Throw MediaException to indicate load failure. throw MediaException( MediaError.Builder() .setDetailedErrorCode(DetailedErrorCode.LOAD_FAILED) .setReason(MediaError.ERROR_REASON_INVALID_REQUEST) .build() ) } myFillMediaInfo(MediaInfoWriter(mediaInfo)) myPlayerLoad(mediaInfo.getContentUrl()) // Update media metadata and state (this clears all previous status // overrides). castReceiverContext.getMediaManager() .setDataFromLoad(loadRequestData) ... castReceiverContext.getMediaManager().broadcastMediaStatus() // Return the resolved MediaLoadRequestData to indicate load success. return loadRequestData } } private fun myPlayerLoad(contentURL: String) { myPlayer.load(contentURL) // Update the MediaSession state. val playbackState: PlaybackStateCompat = Builder() .setState( player.getState(), player.getPosition(), System.currentTimeMillis() ) ... .build() mediaSession.setPlaybackState(playbackState) }
public class MyActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); mediaManager.setMediaLoadCommandCallback(new MyMediaLoadCommandCallback()); } } public class MyMediaLoadCommandCallback extends MediaLoadCommandCallback { @Override public TaskonLoad(String senderId, MediaLoadRequestData loadRequestData) { return Tasks.call(() -> { // Resolve the entity into your data structure and load media. MediaInfo mediaInfo = loadRequestData.getMediaInfo(); if (!checkMediaInfoSupported(mediaInfo)) { // Throw MediaException to indicate load failure. throw new MediaException( new MediaError.Builder() .setDetailedErrorCode(DetailedErrorCode.LOAD_FAILED) .setReason(MediaError.ERROR_REASON_INVALID_REQUEST) .build()); } myFillMediaInfo(new MediaInfoWriter(mediaInfo)); myPlayerLoad(mediaInfo.getContentUrl()); // Update media metadata and state (this clears all previous status // overrides). castReceiverContext.getMediaManager() .setDataFromLoad(loadRequestData); ... castReceiverContext.getMediaManager().broadcastMediaStatus(); // Return the resolved MediaLoadRequestData to indicate load success. return loadRequestData; }); } private void myPlayerLoad(String contentURL) { myPlayer.load(contentURL); // Update the MediaSession state. PlaybackStateCompat playbackState = new PlaybackStateCompat.Builder() .setState( player.getState(), player.getPosition(), System.currentTimeMillis()) ... .build(); mediaSession.setPlaybackState(playbackState); }
Per elaborare l'intent di caricamento, puoi analizzare l'intent nelle strutture di dati
abbiamo definito
(MediaLoadRequestData
)
per le richieste di caricamento).
Supporto dei comandi multimediali
Supporto del controllo di riproduzione di base
I comandi di integrazione di base includono i comandi compatibili con i contenuti multimediali durante la sessione. Questi comandi vengono informati tramite callback delle sessioni multimediali. Devi registrare un callback a una sessione multimediale a supporto (l'operazione potrebbe essere già esistente).
private class MyMediaSessionCallback : MediaSessionCompat.Callback() { override fun onPause() { // Pause the player and update the play state. myPlayer.pause() } override fun onPlay() { // Resume the player and update the play state. myPlayer.play() } override fun onSeekTo(pos: Long) { // Seek and update the play state. myPlayer.seekTo(pos) } ... } mediaSession.setCallback(MyMediaSessionCallback())
private class MyMediaSessionCallback extends MediaSessionCompat.Callback { @Override public void onPause() { // Pause the player and update the play state. myPlayer.pause(); } @Override public void onPlay() { // Resume the player and update the play state. myPlayer.play(); } @Override public void onSeekTo(long pos) { // Seek and update the play state. myPlayer.seekTo(pos); } ... } mediaSession.setCallback(new MyMediaSessionCallback());
Supporto dei comandi di controllo della trasmissione
Alcuni comandi di trasmissione non sono disponibili in
MediaSession
,
come
skipAd()
o
setActiveMediaTracks()
Inoltre, è necessario implementare qui alcuni comandi della coda perché
non è completamente compatibile con la coda MediaSession
.
class MyMediaCommandCallback : MediaCommandCallback() { override fun onSkipAd(requestData: RequestData?): Task<Void?> { // Skip your ad ... return Tasks.forResult(null) } } val mediaManager = CastReceiverContext.getInstance().getMediaManager() mediaManager.setMediaCommandCallback(MyMediaCommandCallback())
public class MyMediaCommandCallback extends MediaCommandCallback { @Override public TaskonSkipAd(RequestData requestData) { // Skip your ad ... return Tasks.forResult(null); } } MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); mediaManager.setMediaCommandCallback(new MyMediaCommandCallback());
Specifica i comandi multimediali supportati
Come nel caso del ricevitore di trasmissione, l'app Android TV deve specificare i comandi
sono supportati, pertanto i mittenti possono attivare o disattivare determinati controlli dell'interfaccia utente. Per
che fanno parte
MediaSession
,
e specificare i comandi
PlaybackStateCompat
.
È necessario specificare altri comandi nel
MediaStatusModifier
// Set media session supported commands val playbackState: PlaybackStateCompat = PlaybackStateCompat.Builder() .setActions(PlaybackStateCompat.ACTION_PLAY or PlaybackStateCompat.ACTION_PAUSE) .setState(PlaybackStateCompat.STATE_PLAYING) .build() mediaSession.setPlaybackState(playbackState) // Set additional commands in MediaStatusModifier val mediaManager = CastReceiverContext.getInstance().getMediaManager() mediaManager.getMediaStatusModifier() .setMediaCommandSupported(MediaStatus.COMMAND_QUEUE_NEXT)
// Set media session supported commands PlaybackStateCompat playbackState = new PlaybackStateCompat.Builder() .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE) .setState(PlaybackStateCompat.STATE_PLAYING) .build(); mediaSession.setPlaybackState(playbackState); // Set additional commands in MediaStatusModifier MediaManager mediaManager = CastReceiverContext.getInstance().getMediaManager(); mediaManager.getMediaStatusModifier() .setMediaCommandSupported(MediaStatus.COMMAND_QUEUE_NEXT);
Nascondi pulsanti non supportati
Se l'app Android TV supporta soltanto il controllo multimediale di base, ma il ricevitore web supporta un controllo più avanzato, devi assicurarti che l'app del mittente si comporti correttamente durante la trasmissione all'app Android TV. Ad esempio, se il tuo dispositivo Android TV non supporta la modifica della velocità di riproduzione a differenza dell'app WebRicevitore, devi impostare correttamente le azioni supportate su ogni piattaforma e assicurarti l'app del mittente esegue correttamente il rendering dell'UI.
Modifica di MediaStatus
Per supportare funzionalità avanzate come tracce, annunci, live streaming e code, il tuo Android
L'app TV deve fornire informazioni aggiuntive che non possono essere stabilite tramite
MediaSession
Forniamo i
MediaStatusModifier
per raggiungere questo obiettivo. MediaStatusModifier
funzionerà sempre su
MediaSession
che hai impostato in
CastReceiverContext
Per creare e trasmettere
MediaStatus
:
val mediaManager: MediaManager = castReceiverContext.getMediaManager() val statusModifier: MediaStatusModifier = mediaManager.getMediaStatusModifier() statusModifier .setLiveSeekableRange(seekableRange) .setAdBreakStatus(adBreakStatus) .setCustomData(customData) mediaManager.broadcastMediaStatus()
MediaManager mediaManager = castReceiverContext.getMediaManager(); MediaStatusModifier statusModifier = mediaManager.getMediaStatusModifier(); statusModifier .setLiveSeekableRange(seekableRange) .setAdBreakStatus(adBreakStatus) .setCustomData(customData); mediaManager.broadcastMediaStatus();
La nostra libreria client riceverà MediaStatus
di base da MediaSession
, il tuo
L'app per Android TV può specificare uno stato aggiuntivo ed eseguire l'override tramite un
Modificatore MediaStatus
.
Alcuni stati e metadati possono essere impostati sia in MediaSession
che in
MediaStatusModifier
. Ti consigliamo vivamente di impostarli solo in
MediaSession
. Puoi comunque utilizzare il modificatore per sostituire gli stati
MediaSession
: questa azione è sconsigliata perché lo stato nel modificatore è sempre
hanno una priorità più alta rispetto ai valori forniti da MediaSession
.
Intercettazione di MediaStatus prima dell'invio
Come l'SDK WebRicevir, se vuoi fare gli ultimi ritocchi prima
puoi specificare
MediaStatusInterceptor
per elaborare
MediaStatus
a
inviate. Passiamo in
MediaStatusWriter
per manipolare MediaStatus
prima che venga inviato.
mediaManager.setMediaStatusInterceptor(object : MediaStatusInterceptor { override fun intercept(mediaStatusWriter: MediaStatusWriter) { // Perform customization. mediaStatusWriter.setCustomData(JSONObject("{data: \"my Hello\"}")) } })
mediaManager.setMediaStatusInterceptor(new MediaStatusInterceptor() { @Override public void intercept(MediaStatusWriter mediaStatusWriter) { // Perform customization. mediaStatusWriter.setCustomData(new JSONObject("{data: \"my Hello\"}")); } });
Gestione delle credenziali utente
La tua app per Android TV potrebbe consentire solo a determinati utenti di avviare o partecipare all'app durante la sessione. Ad esempio, consenti a un mittente di avviare o partecipare solo se:
- L'app del mittente ha eseguito l'accesso allo stesso account e profilo dell'app ATV.
- L'app del mittente ha eseguito l'accesso allo stesso account, ma con un profilo diverso dell'app ATV.
Se la tua app può gestire più utenti o utenti anonimi, puoi consentire qualsiasi a partecipare alla sessione ATV. Se l'utente fornisce le credenziali, l'app ATV deve gestire le sue credenziali in modo che i suoi progressi e altri dati utente possano essere adeguatamente tracciate.
Quando l'app del mittente avvia o partecipa alla tua app per Android TV, l'app del mittente Deve fornire le credenziali che rappresentano gli utenti che partecipano alla sessione.
Prima che un mittente avvii e partecipi alla tua app per Android TV, puoi specificare un avvia il controllo per vedere se le credenziali del mittente sono consentite. In caso contrario, l'app Google Cast Connect SDK esegue l'avvio del ricevitore web.
Dati delle credenziali di lancio dell'app mittente
Per quanto riguarda il mittente, puoi specificare il CredentialsData
per rappresentare chi
partecipare alla sessione.
credentials
è una stringa che può essere definita dall'utente, purché il tuo ATV
che l'app riesca a comprenderlo. credentialsType
definisce su quale piattaforma
CredentialsData
proviene da o può essere un valore personalizzato. Per impostazione predefinita,
alla piattaforma da cui vengono inviati.
Il CredentialsData
viene trasmesso alla tua app Android TV soltanto durante l'avvio o
è ora di partecipare. Se lo imposti di nuovo mentre la connessione è attiva, non verrà trasmesso a
l'app per Android TV. Se il mittente cambia profilo mentre è connesso,
rimanere nella sessione o chiamare
SessionManager.endCurrentCastSession(boolean stopCasting)
se ritieni che il nuovo profilo non sia compatibile con la sessione.
La
CredentialsData
per ciascun mittente possono essere recuperati utilizzando
getSenders
il
CastReceiverContext
per ottenere SenderInfo
,
getCastLaunchRequest()
per ottenere
CastLaunchRequest
,
e poi
getCredentialsData()
.
Richiede la versione play-services-cast-framework
19.0.0
o superiore.
CastContext.getSharedInstance().setLaunchCredentialsData( CredentialsData.Builder() .setCredentials("{\"userId\": \"abc\"}") .build() )
CastContext.getSharedInstance().setLaunchCredentialsData( new CredentialsData.Builder() .setCredentials("{\"userId\": \"abc\"}") .build());
Richiede google-cast-sdk
versione v4.8.1
o
in alto.
Dopo aver impostato le opzioni, puoi chiamarla in qualsiasi momento:
GCKCastContext.setSharedInstanceWith(options)
.
GCKCastContext.sharedInstance().setLaunch( GCKCredentialsData(credentials: "{\"userId\": \"abc\"}")
È necessaria la versione del browser Chromium
M87
o superiore.
Dopo aver impostato le opzioni, puoi chiamarla in qualsiasi momento:
cast.framework.CastContext.getInstance().setOptions(options);
.
let credentialsData = new chrome.cast.CredentialsData("{\"userId\": \"abc\"}"); cast.framework.CastContext.getInstance().setLaunchCredentialsData(credentialsData);
Implementazione dello strumento di controllo richiesta di lancio ATV
La
CredentialsData
viene trasmesso alla tua app Android TV quando un mittente tenta di avviare o partecipare. Puoi
implementare un
LaunchRequestChecker
per consentire o rifiutare questa richiesta.
Se una richiesta viene rifiutata, il ricevitore web viene caricato invece di avviare in modo nativo nell'app ATV. Dovresti rifiutare una richiesta se il tuo ATV non riesce a gestire la richiesta dell'utente di avviare o partecipare. Un esempio potrebbe essere che un altro l'utente ha eseguito l'accesso all'app ATV rispetto a quanto richiesto e la tua app non è in grado di gestire le credenziali di passaggio oppure al momento non esiste un utente che abbia eseguito l'accesso App per ATV.
Se la richiesta è consentita, viene avviata l'app ATV. Puoi personalizzare
a seconda che la tua app supporti l'invio di richieste di carico quando un utente
non abbia effettuato l'accesso all'app ATV o in caso di mancata corrispondenza degli utenti. Questo comportamento è
completamente cusolabile in LaunchRequestChecker
.
Crea un corso che implementa
CastReceiverOptions.LaunchRequestChecker
dell'interfaccia:
class MyLaunchRequestChecker : LaunchRequestChecker { override fun checkLaunchRequestSupported(launchRequest: CastLaunchRequest): Task{ return Tasks.call { myCheckLaunchRequest( launchRequest ) } } } private fun myCheckLaunchRequest(launchRequest: CastLaunchRequest): Boolean { val credentialsData = launchRequest.getCredentialsData() ?: return false // or true if you allow anonymous users to join. // The request comes from a mobile device, e.g. checking user match. return if (credentialsData.credentialsType == CredentialsData.CREDENTIALS_TYPE_ANDROID) { myCheckMobileCredentialsAllowed(credentialsData.getCredentials()) } else false // Unrecognized credentials type. }
public class MyLaunchRequestChecker implements CastReceiverOptions.LaunchRequestChecker { @Override public TaskcheckLaunchRequestSupported(CastLaunchRequest launchRequest) { return Tasks.call(() -> myCheckLaunchRequest(launchRequest)); } } private boolean myCheckLaunchRequest(CastLaunchRequest launchRequest) { CredentialsData credentialsData = launchRequest.getCredentialsData(); if (credentialsData == null) { return false; // or true if you allow anonymous users to join. } // The request comes from a mobile device, e.g. checking user match. if (credentialsData.getCredentialsType().equals(CredentialsData.CREDENTIALS_TYPE_ANDROID)) { return myCheckMobileCredentialsAllowed(credentialsData.getCredentials()); } // Unrecognized credentials type. return false; }
Poi impostalo nel tuo
ReceiverOptionsProvider
:
class MyReceiverOptionsProvider : ReceiverOptionsProvider { override fun getOptions(context: Context?): CastReceiverOptions { return CastReceiverOptions.Builder(context) ... .setLaunchRequestChecker(MyLaunchRequestChecker()) .build() } }
public class MyReceiverOptionsProvider implements ReceiverOptionsProvider { @Override public CastReceiverOptions getOptions(Context context) { return new CastReceiverOptions.Builder(context) ... .setLaunchRequestChecker(new MyLaunchRequestChecker()) .build(); } }
Risoluzione di true
in
LaunchRequestChecker
avvia l'app ATV e false
avvia l'app Web Ricevitore.
Invio & Ricezione di messaggi personalizzati
Il protocollo Cast consente di inviare messaggi di tipo stringa personalizzata tra mittenti e
l'applicazione ricevitore. Devi registrare uno spazio dei nomi (canale) da inviare
dei messaggi prima di inizializzare
CastReceiverContext
Android TV: specifica lo spazio dei nomi personalizzato
Devi specificare gli spazi dei nomi supportati
CastReceiverOptions
durante la configurazione:
class MyReceiverOptionsProvider : ReceiverOptionsProvider { override fun getOptions(context: Context?): CastReceiverOptions { return CastReceiverOptions.Builder(context) .setCustomNamespaces( Arrays.asList("urn:x-cast:com.example.cast.mynamespace") ) .build() } }
public class MyReceiverOptionsProvider implements ReceiverOptionsProvider { @Override public CastReceiverOptions getOptions(Context context) { return new CastReceiverOptions.Builder(context) .setCustomNamespaces( Arrays.asList("urn:x-cast:com.example.cast.mynamespace")) .build(); } }
Android TV: invio di messaggi
// If senderId is null, then the message is broadcasted to all senders. CastReceiverContext.getInstance().sendMessage( "urn:x-cast:com.example.cast.mynamespace", senderId, customString)
// If senderId is null, then the message is broadcasted to all senders. CastReceiverContext.getInstance().sendMessage( "urn:x-cast:com.example.cast.mynamespace", senderId, customString);
Android TV: ricezione di messaggi personalizzati dello spazio dei nomi
class MyCustomMessageListener : MessageReceivedListener { override fun onMessageReceived( namespace: String, senderId: String?, message: String ) { ... } } CastReceiverContext.getInstance().setMessageReceivedListener( "urn:x-cast:com.example.cast.mynamespace", new MyCustomMessageListener());
class MyCustomMessageListener implements CastReceiverContext.MessageReceivedListener { @Override public void onMessageReceived( String namespace, String senderId, String message) { ... } } CastReceiverContext.getInstance().setMessageReceivedListener( "urn:x-cast:com.example.cast.mynamespace", new MyCustomMessageListener());