เพิ่มฟีเจอร์ขั้นสูงในแอป Android

ช่วงพักโฆษณา

Android Sender SDK รองรับช่วงพักโฆษณาและโฆษณาที่แสดงร่วมภายในสตรีมสื่อที่ระบุ

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีการทำงานของช่วงพักโฆษณาได้ที่ภาพรวมช่วงพักโฆษณาใน Webรอรับ

แม้ว่าคุณจะระบุช่วงพักได้ทั้งฝั่งผู้ส่งและผู้รับ แต่เราขอแนะนำให้ระบุช่วงพักโฆษณาในเว็บรีซีฟเวอร์และตัวรับสัญญาณ 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" />
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

องค์ประกอบต่างๆ ของเฟรมเวิร์ก (ได้แก่ กล่องโต้ตอบแคสต์ มินิคอนโทรลเลอร์ และ UIMediaController หากมีการกำหนดค่าไว้) จะแสดงอาร์ตเวิร์กสำหรับสื่อที่กำลังแคสต์อยู่ ปกติแล้ว URL ของอาร์ตเวิร์กรูปภาพจะรวมอยู่ใน MediaMetadata สำหรับสื่อ แต่แอปของผู้ส่งอาจมีแหล่งที่มาสำรองสำหรับ URL ดังกล่าว

คลาส ImagePicker กำหนดวิธีการเลือกรูปภาพที่เหมาะสมจากรายการรูปภาพใน MediaMetadata โดยอิงตามการใช้รูปภาพ เช่น ภาพปกการแจ้งเตือนหรือพื้นหลังแบบเต็มหน้าจอ การใช้งาน ImagePicker เริ่มต้นจะเลือกรูปภาพแรกเสมอ หรือแสดงผลค่าว่างหากไม่มีรูปภาพที่ใช้ได้ใน 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 Sender ของคุณ ตอนนี้คุณสามารถสร้างแอปผู้ส่งสำหรับแพลตฟอร์มอื่น (iOS หรือเว็บ) หรือสร้างแอปเว็บรีซีฟเวอร์ได้แล้ว