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

จัดทุกอย่างให้เป็นระเบียบอยู่เสมอด้วยคอลเล็กชัน บันทึกและจัดหมวดหมู่เนื้อหาตามค่ากำหนดของคุณ

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

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

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

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

ใน Android ให้ระบุช่วงพักโฆษณาในคําสั่งโหลดโดยใช้ AdBreakClipInfo และ AdBreakInfo

โคตลิน
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 ดังนี้

โคตลิน
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 ดังนี้

โคตลิน
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 เพื่อส่งข้อความสตริงไปยังผู้รับผ่านแชแนลดังกล่าวได้ โดยทําดังนี้

โคตลิน
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

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

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