Android アプリに高度な機能を追加する

ミッドロール挿入点

Android Sender SDK は、特定のメディア ストリーム内のミッドロール挿入点とコンパニオン広告をサポートしています。

ミッドロール挿入点の仕組みについて詳しくは、 Web Receiver のミッドロール挿入点の概要をご覧ください。

ミッドロール挿入点は送信側と受信側の両方で指定できますが、プラットフォーム間で一貫した動作を維持するため、Web ReceiverAndroid TV Receiver で指定することをおすすめします。

Android では、 AdBreakClipInfoAdBreakInfoを使用して、読み込みコマンドでミッドロール挿入点を指定します。

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) {
    }
}

カスタム チャネルを追加する

送信側アプリが受信側アプリと通信するには、アプリでカスタム チャネルを作成する必要があります。送信側はカスタム チャネルを使用して、文字列メッセージを受信側に送信できます。各カスタム チャネルは一意の Namespace で定義され、接頭辞 urn:x-cast: で始まる必要があります(例: urn:x-cast:com.example.custom)。複数のカスタム チャネルを使用できます。各チャネルには一意の Namespace があります。受信側アプリは、同じ Namespace を使用してメッセージを送受信することもできます。

カスタム チャネルは 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 ウィジェットの画像選択をオーバーライドする

フレームワークのさまざまなコンポーネント(キャスト ダイアログ、ミニ コントローラ、UIMediaController(構成されている場合))には、現在キャスト中のメディアのアートワークが表示されます。画像アートワークの URL は通常、メディアの MediaMetadata に含まれていますが、送信側アプリに URL の代替ソースがある場合があります。

The ImagePicker クラスは、画像の用途(通知の サムネイルや全画面表示の背景など)に基づいて、MediaMetadata内の画像のリストから適切な画像を選択する方法を定義します。デフォルトの ImagePicker 実装では、常に最初の画像が選択されます。MediaMetadata に画像がない場合は null が返されます。アプリは ImagePicker をサブクラス化し、 onPickImage(MediaMetadata, ImageHints) メソッドをオーバーライドして代替実装を提供できます。その後、 setImagePicker メソッドを使用して、そのサブクラスを 選択します。 CastMediaOptions.BuilderImageHints は、UI に表示する画像のタイプとサイズに関するヒントを ImagePicker に提供します。

キャスト ダイアログをカスタマイズする

セッション ライフサイクルを管理する

SessionManager は、セッション ライフサイクルを管理する中心的な場所です。SessionManager は、Android MediaRouter のルート選択状態の変化をリッスンして、セッションを開始、再開、終了します。ルートが 選択されると、SessionManagerSession オブジェクトを作成し、開始または再開を試みます。ルートが選択解除されると、SessionManager は現在のセッションを終了します。

SessionManager がセッション ライフサイクルを適切に管理するには、次のことを確認する必要があります。

キャスト ダイアログの作成方法によっては、追加の操作が必要になる場合があります。

デバイスがゼロの状態

カスタムの Cast ダイアログを作成する場合は、カスタムの MediaRouteChooserDialog で、デバイスが検出されないケースを適切に処理する必要があります。ダイアログには、アプリがデバイスの検索を試行している場合と、検出の試行がアクティブでない場合をユーザーに明確に示すインジケーターが必要です。

デフォルトの MediaRouteChooserDialog を使用している場合、デバイスがゼロの状態はすでに処理されています。

次のステップ

これで、Android Sender アプリに追加できる機能は終わりです。 別のプラットフォーム(iOS または ウェブ)の送信側アプリを作成するか、 Web Receiver アプリを作成できます。