为您的 Android 应用添加高级功能

广告插播时间点

Android 发送方 SDK 支持在给定媒体串流中投放广告插播和随播广告。

如需详细了解广告插播的运作方式,请参阅网站接收器广告插播概览

虽然您可以在发送器和接收器上指定广告插播时间点,但建议您在 Web 接收器Android TV 接收器上指定广告插播时间点,以便在各个平台上保持一致的行为。

在 Android 上,使用 AdBreakClipInfoAdBreakInfo 在加载命令中指定广告插播时间点:

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 部分。

替换用户体验微件的图片选择

框架的各种组件(即 Cast 对话框、迷你控制器和 UIMediaController,如果已配置)将显示当前投放的媒体的海报图片。图片海报图片的网址通常包含在媒体的 MediaMetadata 中,但发件人应用可能有其他网址来源。

ImagePicker 类定义了一种方法,用于根据图片的用途(例如通知缩略图或全屏背景)从 MediaMetadata 中的图片列表中选择合适的图片。默认的 ImagePicker 实现始终选择第一张图片,如果 MediaMetadata 中没有可用图片,则返回 null。您的应用可以对 ImagePicker 进行子类化并替换 onPickImage(MediaMetadata, ImageHints) 方法以提供备选实现,然后使用 CastMediaOptions.BuildersetImagePicker 方法选择该子类。ImageHints 会向 ImagePicker 提供有关要选择在界面中显示的图片的类型和大小的提示。

自定义 Cast 对话框

管理会话生命周期

SessionManager 是管理会话生命周期的中心位置。SessionManager 会监听 Android MediaRouter 路线选择状态变化,以启动、恢复和结束会话。选择路线后,SessionManager 会创建 Session 对象,并尝试启动或恢复该对象。取消选择路线后,SessionManager 会结束当前会话。

因此,为确保 SessionManager 能够正确管理会话生命周期,您必须确保:

根据您创建投放对话框的方式,您可能需要执行其他操作:

零设备状态

如果您创建自定义 Cast 对话框,则自定义 MediaRouteChooserDialog 应正确处理找不到任何设备的情况。该对话框应包含指示器,以便用户清楚了解应用何时仍在尝试查找设备,以及何时不再尝试查找设备。

如果您使用的是默认的 MediaRouteChooserDialog,则系统已处理零设备状态。

后续步骤

至此,您可以向 Android 发件人应用添加的功能已全部介绍完毕。现在,您可以为其他平台(iOSWeb)构建发件人应用,也可以构建Web 接收器应用