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

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

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

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

แม้ว่าจะระบุช่วงพักได้ทั้งในฝั่งผู้ส่งและฝั่งผู้รับ แต่เราขอแนะนำให้ระบุใน Web Receiver และ Android TV Receiver เพื่อให้ลักษณะการทำงานสอดคล้องกันในทุกแพลตฟอร์ม

ใน 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

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

คลาส ImagePicker จะกำหนดวิธีการเลือกรูปภาพที่เหมาะสมจากรายการรูปภาพใน MediaMetadata โดยอิงตามการใช้งานรูปภาพ เช่น ภาพขนาดย่อของการแจ้งเตือนหรือพื้นหลังแบบเต็มหน้าจอ การติดตั้งใช้งาน ImagePicker เริ่มต้นจะเลือกรูปภาพแรกเสมอ หรือแสดงผลเป็น Null หากไม่มีรูปภาพใน MediaMetadata แอปของคุณสามารถสร้างคลาสย่อย ImagePicker และลบล้างเมธอด onPickImage(MediaMetadata, ImageHints) เพื่อให้การติดตั้งใช้งานอื่น จากนั้นเลือกคลาสย่อยนั้นด้วยเมธอด setImagePicker ของ CastMediaOptions.Builder ImageHints ให้คำแนะนำแก่ ImagePicker เกี่ยวกับประเภทและขนาดของรูปภาพที่จะเลือกเพื่อแสดงใน UI

การปรับแต่งกล่องโต้ตอบ Cast

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

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