Как добавить в приложение Android продвинутые функции

Рекламные паузы

Android Sender SDK поддерживает рекламные паузы и сопутствующие объявления в медиаконтенте.

Дополнительную информацию о том, как работают рекламные паузы, можно найти в обзоре рекламных пауз веб-приемника.

Хотя перерывы можно задавать как на отправителе, так и на получателе, мы рекомендуем делать это на веб-получателе и получателе Android TV, чтобы обеспечить единообразное поведение на разных платформах.

На устройствах Android рекламные паузы в команде загрузки задаются с помощью параметров AdBreakClipInfo и AdBreakInfo:

Kotlin
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)
Java
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" />
Kotlin
// 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) {
    }
}
Java
// 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:

Kotlin
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")
    }
}
Java
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:

Kotlin
try {
    mCastSession.setMessageReceivedCallbacks(
        mHelloWorldChannel.namespace,
        mHelloWorldChannel)
} catch (e: IOException) {
    Log.e(TAG, "Exception while creating channel", e)
}
Java
try {
    mCastSession.setMessageReceivedCallbacks(
            mHelloWorldChannel.getNamespace(),
            mHelloWorldChannel);
} catch (IOException e) {
    Log.e(TAG, "Exception while creating channel", e);
}

После создания специального канала отправитель может использовать метод sendMessage, чтобы отправлять получателю строковые сообщения по этому каналу:

Kotlin
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)
        }
    }
}
Java
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 отслеживает изменения состояния выбора маршрута в AndroidMediaRouter, чтобы начинать, возобновлять и завершать сеансы. Когда маршрут выбран, SessionManager создает объект Session и пытается запустить или возобновить его. Если маршрут отменен, SessionManager завершит текущий сеанс.

Чтобы SessionManager правильно управлял жизненными циклами сеансов, необходимо обеспечить следующее:

В зависимости от того, как вы создаете диалоговые окна Cast, могут потребоваться дополнительные действия:

  • Если вы создаете диалоговые окна Cast с помощью MediaRouteChooserDialog и MediaRouteControllerDialog, то эти диалоговые окна будут автоматически обновлять выбор маршрута в MediaRouter, поэтому ничего делать не нужно.
  • Если вы настроили кнопку трансляции с помощью CastButtonFactory.setUpMediaRouteButton(Context, Menu, int) или CastButtonFactory.setUpMediaRouteButton(Context, MediaRouteButton), то диалоговые окна создаются с помощью MediaRouteChooserDialog и MediaRouteControllerDialog, поэтому вам ничего не нужно делать.
  • В остальных случаях вам нужно будет создать собственные диалоговые окна Cast, поэтому следуйте приведенным выше инструкциям, чтобы обновить состояние выбора маршрута в MediaRouter.

Состояние "Нет устройств"

Если вы создаете собственные диалоговые окна Cast, то в них MediaRouteChooserDialog должна быть предусмотрена обработка ситуации, когда не найдено ни одного устройства. В диалоговом окне должны быть индикаторы, которые показывают пользователям, когда приложение пытается найти устройства, а когда попытка поиска больше не активна.

Если вы используете MediaRouteChooserDialog по умолчанию, то состояние, когда устройств нет, уже обработано.

Дальнейшие действия

Это все функции, которые можно добавить в приложение-отправитель для Android. Теперь вы можете создать приложение-отправитель для другой платформы (iOS или веб-приложение) или веб-приложение-приемник.