เพิ่มฟีเจอร์ขั้นสูงในแอป 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 ของคุณเอง คุณจะต้องเพิ่มลงในไฟล์ Manifest และตั้งชื่อแท็กใน 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) {
    }
}
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 เกี่ยวกับประเภทและขนาดของรูปภาพที่จะเลือกแสดงใน UI

การปรับแต่งกล่องโต้ตอบการแคสต์

การจัดการวงจรเซสชัน

SessionManager เป็นศูนย์กลางในการจัดการวงจรเซสชัน SessionManager รอฟัง Android MediaRouter สถานะการเลือกเส้นทางจะเปลี่ยนเพื่อเริ่มเซสชันต่อ และสิ้นสุดเซสชัน เมื่อเลือกเส้นทางแล้ว SessionManager จะสร้างออบเจ็กต์ Session และพยายามเริ่มต้นหรือดำเนินการต่อ เมื่อยกเลิกการเลือกเส้นทาง SessionManager จะหยุดเซสชันปัจจุบัน

ดังนั้นเพื่อให้ SessionManager จัดการวงจรเซสชันอย่างถูกต้อง คุณต้องตรวจสอบว่า

คุณอาจต้องดำเนินการเพิ่มเติมต่อไปนี้ ทั้งนี้ขึ้นอยู่กับวิธีสร้างกล่องโต้ตอบการแคสต์

  • หากคุณสร้างกล่องโต้ตอบการแคสต์โดยใช้ MediaRouteChooserDialog และ MediaRouteControllerDialog กล่องโต้ตอบเหล่านี้จะอัปเดตการเลือกเส้นทางใน MediaRouter โดยอัตโนมัติ คุณจึงไม่ต้องดำเนินการใดๆ
  • หากคุณตั้งค่าปุ่ม "แคสต์" โดยใช้ CastButtonFactory.setUpMediaRouteButton(Context, Menu, int) หรือ CastButtonFactory.setUpMediaRouteButton(Context, MediaRouteButton) กล่องโต้ตอบจะสร้างขึ้น โดยใช้ MediaRouteChooserDialog และ MediaRouteControllerDialog คุณจึงไม่จำเป็นต้องดำเนินการใดๆ อีก
  • สำหรับกรณีอื่นๆ คุณจะสร้างกล่องโต้ตอบการแคสต์ที่กำหนดเอง คุณจึงต้องทำตามวิธีการข้างต้นเพื่ออัปเดตสถานะการเลือกเส้นทางใน MediaRouter

สถานะอุปกรณ์เป็นศูนย์

หากคุณสร้างกล่องโต้ตอบการแคสต์ที่กำหนดเอง MediaRouteChooserDialog ที่คุณกำหนดเองควรจัดการกรณีที่พบอุปกรณ์ 0 เครื่องได้อย่างเหมาะสม กล่องโต้ตอบควรมีสัญญาณบอกสถานะให้ผู้ใช้ทราบอย่างชัดเจนเมื่อแอปกำลังพยายามค้นหาอุปกรณ์อยู่ และเมื่อไม่มีการค้นหาอุปกรณ์อีกแล้ว

หากคุณใช้ MediaRouteChooserDialog เริ่มต้น ระบบจะจัดการกับสถานะอุปกรณ์เป็น 0 อยู่แล้ว

ขั้นตอนถัดไป

ส่วนนี้ของฟีเจอร์ที่คุณสามารถเพิ่มลงในแอปผู้ส่งบน Android ได้ ตอนนี้คุณสร้างแอปผู้ส่งสำหรับแพลตฟอร์มอื่นได้ (iOS หรือเว็บ) หรือสร้างแอป Web Receiver