Рекламные паузы
Android Sender SDK обеспечивает поддержку рекламных пауз и сопутствующей рекламы в заданном медиапотоке.
Дополнительную информацию о работе рекламных пауз см. в обзоре рекламных пауз Web Receiver .
Хотя перерывы можно указывать как на отправителе, так и на получателе, рекомендуется указывать их на веб-приемнике и приемнике Android TV, чтобы обеспечить единообразное поведение на разных платформах.
На Android укажите рекламные паузы в команде загрузки с помощью AdBreakClipInfo
и AdBreakInfo
:
val breakClip1: AdBreakClipInfo = AdBreakClipInfo.Builder("bc0") .setTitle("Clip title") .setPosterUrl("https://www.some.url") .setDuration(60000) .setWhenSkippableInMs(5000) // Set this field so that the ad is skippable .build() val breakClip2: AdBreakClipInfo = … val breakClip3: AdBreakClipInfo = … val break1: AdBreakClipInfo = AdBreakInfo.Builder(/* playbackPositionInMs= */ 10000) .setId("b0") .setBreakClipIds({"bc0","bc1","bc2"}) … .build() val mediaInfo: MediaInfo = MediaInfo.Builder() … .setAdBreaks({break1}) .setAdBreakClips({breakClip1, breakClip2, breakClip3}) .build() val mediaLoadRequestData: MediaLoadRequestData = MediaInfo.Builder() … .setMediaInfo(mediaInfo) .build() remoteMediaClient.load(mediaLoadRequestData)
AdBreakClipInfo breakClip1 = new AdBreakClipInfo.Builder("bc0") .setTitle("Clip title") .setPosterUrl("https://www.some.url") .setDuration(60000) .setWhenSkippableInMs(5000) // Set this field so that the ad is skippable .build(); AdBreakClipInfo breakClip2 = … AdBreakClipInfo breakClip3 = … AdBreakInfo break1 = new AdBreakInfo.Builder(/* playbackPositionInMs= */ 10000) .setId("b0") .setBreakClipIds({"bc0","bc1","bc2"}) … .build(); MediaInfo mediaInfo = new MediaInfo.Builder() … .setAdBreaks({break1}) .setAdBreakClips({breakClip1, breakClip2, breakClip3}) .build(); MediaLoadRequestData mediaLoadRequestData = new MediaInfo.Builder() … .setMediaInfo(mediaInfo) .build(); remoteMediaClient.load(mediaLoadRequestData);
Добавить пользовательские действия
Приложение-отправитель может расширить MediaIntentReceiver
для обработки пользовательских действий или переопределения его поведения. Если вы реализовали свой собственный MediaIntentReceiver
, вам необходимо добавить его в манифест, а также задать его имя в CastMediaOptions
. В этом примере представлены пользовательские действия, которые переопределяют переключение удаленного воспроизведения мультимедиа, нажатие кнопки мультимедиа и другие типы действий.
// In AndroidManifest.xml
<receiver android:name="com.example.MyMediaIntentReceiver" />
// In your OptionsProvider var mediaOptions = CastMediaOptions.Builder() .setMediaIntentReceiverClassName(MyMediaIntentReceiver::class.java.name) .build() // Implementation of MyMediaIntentReceiver internal class MyMediaIntentReceiver : MediaIntentReceiver() { override fun onReceiveActionTogglePlayback(currentSession: Session) { } override fun onReceiveActionMediaButton(currentSession: Session, intent: Intent) { } override fun onReceiveOtherAction(context: Context?, action: String, intent: Intent) { } }
// In your OptionsProvider CastMediaOptions mediaOptions = new CastMediaOptions.Builder() .setMediaIntentReceiverClassName(MyMediaIntentReceiver.class.getName()) .build(); // Implementation of MyMediaIntentReceiver class MyMediaIntentReceiver extends MediaIntentReceiver { @Override protected void onReceiveActionTogglePlayback(Session currentSession) { } @Override protected void onReceiveActionMediaButton(Session currentSession, Intent intent) { } @Override protected void onReceiveOtherAction(Context context, String action, Intent intent) { } }
Добавить пользовательский канал
Чтобы приложение-отправитель могло взаимодействовать с приложением-получателем, вашему приложению необходимо создать настраиваемый канал. Отправитель может использовать настраиваемый канал для отправки строковых сообщений получателю. Каждый настраиваемый канал определяется уникальным пространством имен и должен начинаться с префикса urn:x-cast:
, например, urn:x-cast:com.example.custom
. Возможно иметь несколько настраиваемых каналов, каждый с уникальным пространством имен. Приложение-получатель также может отправлять и получать сообщения, используя одно и то же пространство имен.
Пользовательский канал реализован с помощью интерфейса Cast.MessageReceivedCallback
:
class HelloWorldChannel : MessageReceivedCallback { val namespace: String get() = "urn:x-cast:com.example.custom" override fun onMessageReceived(castDevice: CastDevice, namespace: String, message: String) { Log.d(TAG, "onMessageReceived: $message") } }
class HelloWorldChannel implements Cast.MessageReceivedCallback { public String getNamespace() { return "urn:x-cast:com.example.custom"; } @Override public void onMessageReceived(CastDevice castDevice, String namespace, String message) { Log.d(TAG, "onMessageReceived: " + message); } }
После подключения приложения-отправителя к приложению-получателю можно создать настраиваемый канал с помощью метода setMessageReceivedCallbacks
:
try { mCastSession.setMessageReceivedCallbacks( mHelloWorldChannel.namespace, mHelloWorldChannel) } catch (e: IOException) { Log.e(TAG, "Exception while creating channel", e) }
try { mCastSession.setMessageReceivedCallbacks( mHelloWorldChannel.getNamespace(), mHelloWorldChannel); } catch (IOException e) { Log.e(TAG, "Exception while creating channel", e); }
После создания пользовательского канала отправитель может использовать метод sendMessage
для отправки строковых сообщений получателю по этому каналу:
private fun sendMessage(message: String) { if (mHelloWorldChannel != null) { try { mCastSession.sendMessage(mHelloWorldChannel.namespace, message) .setResultCallback { status -> if (!status.isSuccess) { Log.e(TAG, "Sending message failed") } } } catch (e: Exception) { Log.e(TAG, "Exception while sending message", e) } } }
private void sendMessage(String message) { if (mHelloWorldChannel != null) { try { mCastSession.sendMessage(mHelloWorldChannel.getNamespace(), message) .setResultCallback( status -> { if (!status.isSuccess()) { Log.e(TAG, "Sending message failed"); } }); } catch (Exception e) { Log.e(TAG, "Exception while sending message", e); } } }
Поддержка автовоспроизведения
См. раздел API автозапуска и очередей .
Переопределить выбор изображения для виджетов UX
Различные компоненты фреймворка (а именно диалог Cast, мини-контроллер и UIMediaController, если настроены соответствующим образом) будут отображать обложку для текущего транслируемого медиа. URL-адреса обложки изображения обычно включаются в MediaMetadata
для медиа, но у приложения-отправителя может быть альтернативный источник для URL-адресов.
Класс ImagePicker
определяет средство выбора подходящего изображения из списка изображений в MediaMetadata
на основе использования изображения, например, миниатюры уведомления или полноэкранного фона. Реализация ImagePicker
по умолчанию всегда выбирает первое изображение или возвращает null, если в MediaMetadata
нет доступного изображения. Ваше приложение может создать подкласс ImagePicker
и переопределить метод onPickImage(MediaMetadata, ImageHints)
для предоставления альтернативной реализации, а затем выбрать этот подкласс с помощью метода setImagePicker
из CastMediaOptions.Builder
. ImageHints
предоставляет подсказки для ImagePicker
о типе и размере изображения, которое будет выбрано для отображения в пользовательском интерфейсе.
Настройка диалогов Cast
Управление жизненным циклом сеанса
SessionManager
— это центральное место для управления жизненным циклом сеанса. SessionManager
прослушивает изменения состояния выбора маршрута Android MediaRouter
для запуска, возобновления и завершения сеансов. Когда маршрут выбран, SessionManager
создаст объект Session
и попытается запустить или возобновить его. Когда маршрут не выбран, SessionManager
завершит текущий сеанс.
Поэтому, чтобы гарантировать, что SessionManager
правильно управляет жизненными циклами сеансов, необходимо убедиться, что:
- В диалоговом окне выбора маршрута вызовите
MediaRouter.selectRoute(MediaRouter.RouteInfo)
, когда пользователь выбирает устройство. - В диалоговом окне контроллера маршрута (в состоянии подключения или трансляции ) вызовите
MediaRouter.unselect(int)
, когда пользователь останавливает трансляцию.
В зависимости от того, как вы создаете диалоговые окна Cast, могут потребоваться дополнительные действия:
- Если вы создаете диалоговые окна трансляции с помощью
MediaRouteChooserDialog
иMediaRouteControllerDialog
, то эти диалоговые окна автоматически обновят выбор маршрута вMediaRouter
, поэтому ничего делать не нужно. - Если вы настраиваете кнопку Cast с помощью
CastButtonFactory.setUpMediaRouteButton(Context, Menu, int)
илиCastButtonFactory.setUpMediaRouteButton(Context, MediaRouteButton)
, то диалоговые окна фактически создаются с помощьюMediaRouteChooserDialog
иMediaRouteControllerDialog
, поэтому ничего делать не нужно. - В других случаях вы будете создавать пользовательские диалоговые окна Cast, поэтому вам необходимо следовать приведенным выше инструкциям, чтобы обновить состояние выбора маршрута в
MediaRouter
.
Состояние нулевых устройств
Если вы создаете пользовательские диалоги Cast, ваш пользовательский MediaRouteChooserDialog
должен правильно обрабатывать случай нулевого обнаружения устройств. Диалог должен иметь индикаторы, которые дают понять вашим пользователям, когда ваше приложение все еще пытается найти устройства, а когда попытка обнаружения больше не активна.
Если вы используете MediaRouteChooserDialog
по умолчанию, состояние нулевых устройств уже обработано.
Следующие шаги
На этом завершаются функции, которые вы можете добавить в свое приложение Android Sender. Теперь вы можете создать приложение-отправитель для другой платформы ( iOS или Web ) или создать приложение Web Receiver .
,Рекламные паузы
Android Sender SDK обеспечивает поддержку рекламных пауз и сопутствующей рекламы в заданном медиапотоке.
Дополнительную информацию о работе рекламных пауз см. в обзоре рекламных пауз Web Receiver .
Хотя перерывы можно указывать как на отправителе, так и на получателе, рекомендуется указывать их на веб-приемнике и приемнике Android TV, чтобы обеспечить единообразное поведение на разных платформах.
На Android укажите рекламные паузы в команде загрузки с помощью AdBreakClipInfo
и AdBreakInfo
:
val breakClip1: AdBreakClipInfo = AdBreakClipInfo.Builder("bc0") .setTitle("Clip title") .setPosterUrl("https://www.some.url") .setDuration(60000) .setWhenSkippableInMs(5000) // Set this field so that the ad is skippable .build() val breakClip2: AdBreakClipInfo = … val breakClip3: AdBreakClipInfo = … val break1: AdBreakClipInfo = AdBreakInfo.Builder(/* playbackPositionInMs= */ 10000) .setId("b0") .setBreakClipIds({"bc0","bc1","bc2"}) … .build() val mediaInfo: MediaInfo = MediaInfo.Builder() … .setAdBreaks({break1}) .setAdBreakClips({breakClip1, breakClip2, breakClip3}) .build() val mediaLoadRequestData: MediaLoadRequestData = MediaInfo.Builder() … .setMediaInfo(mediaInfo) .build() remoteMediaClient.load(mediaLoadRequestData)
AdBreakClipInfo breakClip1 = new AdBreakClipInfo.Builder("bc0") .setTitle("Clip title") .setPosterUrl("https://www.some.url") .setDuration(60000) .setWhenSkippableInMs(5000) // Set this field so that the ad is skippable .build(); AdBreakClipInfo breakClip2 = … AdBreakClipInfo breakClip3 = … AdBreakInfo break1 = new AdBreakInfo.Builder(/* playbackPositionInMs= */ 10000) .setId("b0") .setBreakClipIds({"bc0","bc1","bc2"}) … .build(); MediaInfo mediaInfo = new MediaInfo.Builder() … .setAdBreaks({break1}) .setAdBreakClips({breakClip1, breakClip2, breakClip3}) .build(); MediaLoadRequestData mediaLoadRequestData = new MediaInfo.Builder() … .setMediaInfo(mediaInfo) .build(); remoteMediaClient.load(mediaLoadRequestData);
Добавить пользовательские действия
Приложение-отправитель может расширить MediaIntentReceiver
для обработки пользовательских действий или переопределения его поведения. Если вы реализовали свой собственный MediaIntentReceiver
, вам необходимо добавить его в манифест, а также задать его имя в CastMediaOptions
. В этом примере представлены пользовательские действия, которые переопределяют переключение удаленного воспроизведения мультимедиа, нажатие кнопки мультимедиа и другие типы действий.
// In AndroidManifest.xml
<receiver android:name="com.example.MyMediaIntentReceiver" />
// In your OptionsProvider var mediaOptions = CastMediaOptions.Builder() .setMediaIntentReceiverClassName(MyMediaIntentReceiver::class.java.name) .build() // Implementation of MyMediaIntentReceiver internal class MyMediaIntentReceiver : MediaIntentReceiver() { override fun onReceiveActionTogglePlayback(currentSession: Session) { } override fun onReceiveActionMediaButton(currentSession: Session, intent: Intent) { } override fun onReceiveOtherAction(context: Context?, action: String, intent: Intent) { } }
// In your OptionsProvider CastMediaOptions mediaOptions = new CastMediaOptions.Builder() .setMediaIntentReceiverClassName(MyMediaIntentReceiver.class.getName()) .build(); // Implementation of MyMediaIntentReceiver class MyMediaIntentReceiver extends MediaIntentReceiver { @Override protected void onReceiveActionTogglePlayback(Session currentSession) { } @Override protected void onReceiveActionMediaButton(Session currentSession, Intent intent) { } @Override protected void onReceiveOtherAction(Context context, String action, Intent intent) { } }
Добавить пользовательский канал
Чтобы приложение-отправитель могло взаимодействовать с приложением-получателем, вашему приложению необходимо создать настраиваемый канал. Отправитель может использовать настраиваемый канал для отправки строковых сообщений получателю. Каждый настраиваемый канал определяется уникальным пространством имен и должен начинаться с префикса urn:x-cast:
, например, urn:x-cast:com.example.custom
. Возможно иметь несколько настраиваемых каналов, каждый с уникальным пространством имен. Приложение-получатель также может отправлять и получать сообщения, используя одно и то же пространство имен.
Пользовательский канал реализован с помощью интерфейса Cast.MessageReceivedCallback
:
class HelloWorldChannel : MessageReceivedCallback { val namespace: String get() = "urn:x-cast:com.example.custom" override fun onMessageReceived(castDevice: CastDevice, namespace: String, message: String) { Log.d(TAG, "onMessageReceived: $message") } }
class HelloWorldChannel implements Cast.MessageReceivedCallback { public String getNamespace() { return "urn:x-cast:com.example.custom"; } @Override public void onMessageReceived(CastDevice castDevice, String namespace, String message) { Log.d(TAG, "onMessageReceived: " + message); } }
После подключения приложения-отправителя к приложению-получателю можно создать настраиваемый канал с помощью метода setMessageReceivedCallbacks
:
try { mCastSession.setMessageReceivedCallbacks( mHelloWorldChannel.namespace, mHelloWorldChannel) } catch (e: IOException) { Log.e(TAG, "Exception while creating channel", e) }
try { mCastSession.setMessageReceivedCallbacks( mHelloWorldChannel.getNamespace(), mHelloWorldChannel); } catch (IOException e) { Log.e(TAG, "Exception while creating channel", e); }
После создания пользовательского канала отправитель может использовать метод sendMessage
для отправки строковых сообщений получателю по этому каналу:
private fun sendMessage(message: String) { if (mHelloWorldChannel != null) { try { mCastSession.sendMessage(mHelloWorldChannel.namespace, message) .setResultCallback { status -> if (!status.isSuccess) { Log.e(TAG, "Sending message failed") } } } catch (e: Exception) { Log.e(TAG, "Exception while sending message", e) } } }
private void sendMessage(String message) { if (mHelloWorldChannel != null) { try { mCastSession.sendMessage(mHelloWorldChannel.getNamespace(), message) .setResultCallback( status -> { if (!status.isSuccess()) { Log.e(TAG, "Sending message failed"); } }); } catch (Exception e) { Log.e(TAG, "Exception while sending message", e); } } }
Поддержка автовоспроизведения
См. раздел API автозапуска и очередей .
Переопределить выбор изображения для виджетов UX
Различные компоненты фреймворка (а именно диалог Cast, мини-контроллер и UIMediaController, если настроены соответствующим образом) будут отображать обложку для текущего транслируемого медиа. URL-адреса обложки изображения обычно включаются в MediaMetadata
для медиа, но у приложения-отправителя может быть альтернативный источник для URL-адресов.
Класс ImagePicker
определяет средство выбора подходящего изображения из списка изображений в MediaMetadata
на основе использования изображения, например, миниатюры уведомления или полноэкранного фона. Реализация ImagePicker
по умолчанию всегда выбирает первое изображение или возвращает null, если в MediaMetadata
нет доступного изображения. Ваше приложение может создать подкласс ImagePicker
и переопределить метод onPickImage(MediaMetadata, ImageHints)
для предоставления альтернативной реализации, а затем выбрать этот подкласс с помощью метода setImagePicker
из CastMediaOptions.Builder
. ImageHints
предоставляет подсказки для ImagePicker
о типе и размере изображения, которое будет выбрано для отображения в пользовательском интерфейсе.
Настройка диалогов Cast
Управление жизненным циклом сеанса
SessionManager
— это центральное место для управления жизненным циклом сеанса. SessionManager
прослушивает изменения состояния выбора маршрута Android MediaRouter
для запуска, возобновления и завершения сеансов. Когда маршрут выбран, SessionManager
создаст объект Session
и попытается запустить или возобновить его. Когда маршрут не выбран, SessionManager
завершит текущий сеанс.
Поэтому, чтобы гарантировать, что SessionManager
правильно управляет жизненными циклами сеансов, необходимо убедиться, что:
- В диалоговом окне выбора маршрута вызовите
MediaRouter.selectRoute(MediaRouter.RouteInfo)
, когда пользователь выбирает устройство. - В диалоговом окне контроллера маршрута (в состоянии подключения или трансляции ) вызовите
MediaRouter.unselect(int)
, когда пользователь останавливает трансляцию.
В зависимости от того, как вы создаете диалоговые окна Cast, могут потребоваться дополнительные действия:
- Если вы создаете диалоговые окна трансляции с помощью
MediaRouteChooserDialog
иMediaRouteControllerDialog
, то эти диалоговые окна автоматически обновят выбор маршрута вMediaRouter
, поэтому ничего делать не нужно. - Если вы настраиваете кнопку Cast с помощью
CastButtonFactory.setUpMediaRouteButton(Context, Menu, int)
илиCastButtonFactory.setUpMediaRouteButton(Context, MediaRouteButton)
, то диалоговые окна фактически создаются с помощьюMediaRouteChooserDialog
иMediaRouteControllerDialog
, поэтому ничего делать не нужно. - В других случаях вы будете создавать пользовательские диалоговые окна Cast, поэтому вам необходимо следовать приведенным выше инструкциям, чтобы обновить состояние выбора маршрута в
MediaRouter
.
Состояние нулевых устройств
Если вы создаете пользовательские диалоги Cast, ваш пользовательский MediaRouteChooserDialog
должен правильно обрабатывать случай нулевого обнаружения устройств. Диалог должен иметь индикаторы, которые дают понять вашим пользователям, когда ваше приложение все еще пытается найти устройства, а когда попытка обнаружения больше не активна.
Если вы используете MediaRouteChooserDialog
по умолчанию, состояние нулевых устройств уже обработано.
Следующие шаги
На этом завершаются функции, которые вы можете добавить в свое приложение Android Sender. Теперь вы можете создать приложение-отправитель для другой платформы ( iOS или Web ) или создать приложение Web Receiver .