Phát triển dịch vụ đầu vào TV

Dịch vụ đầu vào TV đại diện cho một nguồn luồng nội dung nghe nhìn và cho phép bạn trình bày nội dung nghe nhìn theo kiểu truyền hình phát sóng tuyến tính dưới dạng các kênh và chương trình. Với dịch vụ đầu vào của TV, bạn có thể cung cấp chế độ kiểm soát của cha mẹ, thông tin về hướng dẫn chương trình và mức phân loại nội dung. Dịch vụ đầu vào TV hoạt động với ứng dụng truyền hình của hệ thống Android. Ứng dụng này kiểm soát và trình bày nội dung kênh trên TV. Ứng dụng truyền hình hệ thống được phát triển riêng cho thiết bị và không thể thay đổi bởi các ứng dụng bên thứ ba. Để biết thêm thông tin về cấu trúc TV Input Framework (TIF) và các thành phần của cấu trúc này, hãy xem phần TV Input Framework.

Tạo dịch vụ đầu vào TV bằng cách sử dụng Thư viện TIF Companion.

TIF Companion Library là một khung cung cấp các cách triển khai có thể mở rộng của các tính năng phổ biến trong dịch vụ đầu vào của TV. Công cụ này chỉ được thiết kế để các nhà sản xuất thiết bị gốc (OEM) xây dựng kênh hỗ trợ cho Android 5.0 (API level 21) đến Android 7.1 (API level 25).

Cập nhật dự án của bạn

Thư viện TIF Companion có sẵn để các nhà sản xuất thiết bị gốc (OEM) sử dụng cho mục đích cũ trong kho lưu trữ androidtv-sample-inputs. Hãy xem kho lưu trữ đó để biết ví dụ về cách tích hợp thư viện vào ứng dụng.

Khai báo dịch vụ đầu vào TV trong tệp kê khai

Ứng dụng của bạn phải cung cấp một dịch vụ tương thích với TvInputService mà hệ thống dùng để truy cập vào ứng dụng của bạn. Thư viện đồng hành TIF cung cấp lớp BaseTvInputService, cung cấp một chế độ triển khai mặc định của TvInputService mà bạn có thể tuỳ chỉnh. Tạo một lớp con của BaseTvInputService và khai báo lớp con đó trong tệp kê khai dưới dạng một dịch vụ.

Trong nội dung khai báo tệp kê khai, hãy chỉ định quyền BIND_TV_INPUT để cho phép dịch vụ kết nối đầu vào TV với hệ thống. Một dịch vụ hệ thống thực hiện việc liên kết và có quyền BIND_TV_INPUT. Ứng dụng truyền hình của hệ thống gửi yêu cầu đến các dịch vụ đầu vào TV thông qua giao diện TvInputManager.

Trong nội dung khai báo dịch vụ, hãy thêm một bộ lọc ý định chỉ định TvInputService làm thao tác cần thực hiện với ý định. Ngoài ra, hãy khai báo siêu dữ liệu dịch vụ dưới dạng một tài nguyên XML riêng biệt. Ví dụ sau đây minh họa cách khai báo dịch vụ, bộ lọc ý định và khai báo siêu dữ liệu dịch vụ:

<service android:name=".rich.RichTvInputService"
    android:label="@string/rich_input_label"
    android:permission="android.permission.BIND_TV_INPUT">
    <!-- Required filter used by the system to launch our account service. -->
    <intent-filter>
        <action android:name="android.media.tv.TvInputService" />
    </intent-filter>
    <!-- An XML file which describes this input. This provides pointers to
    the RichTvInputSetupActivity to the system/TV app. -->
    <meta-data
        android:name="android.media.tv.input"
        android:resource="@xml/richtvinputservice" />
</service>

Hãy định nghĩa siêu dữ liệu dịch vụ trong một tệp XML riêng biệt. Tệp XML siêu dữ liệu dịch vụ phải có một giao diện thiết lập mô tả cấu hình ban đầu và quá trình quét kênh của đầu vào TV. Tệp siêu dữ liệu cũng phải chứa một cờ cho biết người dùng có thể ghi lại nội dung hay không. Để biết thêm thông tin về cách hỗ trợ ghi nội dung trong ứng dụng, hãy xem bài viết Hỗ trợ ghi nội dung.

Tệp siêu dữ liệu dịch vụ nằm trong thư mục tài nguyên XML của ứng dụng và phải khớp với tên của tài nguyên mà bạn đã khai báo trong tệp kê khai. Khi sử dụng các mục kê khai trong ví dụ trước, bạn sẽ tạo tệp XML tại res/xml/richtvinputservice.xml, với nội dung sau:

<?xml version="1.0" encoding="utf-8"?>
<tv-input xmlns:android="http://schemas.android.com/apk/res/android"
  android:canRecord="true"
  android:setupActivity="com.example.android.sampletvinput.rich.RichTvInputSetupActivity" />

Xác định các kênh và tạo hoạt động thiết lập

Dịch vụ đầu vào TV phải xác định ít nhất một kênh mà người dùng truy cập thông qua ứng dụng truyền hình hệ thống. Bạn nên đăng ký các kênh của mình trong cơ sở dữ liệu hệ thống và cung cấp một hoạt động thiết lập mà hệ thống sẽ gọi khi không tìm thấy kênh nào cho ứng dụng của bạn.

Trước tiên, hãy cho phép ứng dụng của bạn đọc và ghi vào Hướng dẫn chương trình điện tử (EPG) của hệ thống. Dữ liệu của EPG bao gồm các kênh và chương trình mà người dùng có thể xem. Để ứng dụng của bạn có thể thực hiện các thao tác này và đồng bộ với EPG sau khi thiết bị khởi động lại, hãy thêm các phần tử sau vào tệp kê khai ứng dụng của bạn:

<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED "/>

Thêm phần tử sau để đảm bảo ứng dụng của bạn hiển thị trên Cửa hàng Google Play với tư cách là ứng dụng cung cấp các kênh nội dung trên Android TV:

<uses-feature
    android:name="android.software.live_tv"
    android:required="true" />

Tiếp theo, hãy tạo một lớp mở rộng lớp EpgSyncJobService. Lớp trừu tượng này cho phép bạn tạo một dịch vụ công việc tạo và cập nhật các kênh trong cơ sở dữ liệu hệ thống.

Trong lớp con của bạn, hãy tạo và trả về danh sách đầy đủ các kênh trong getChannels. Nếu các kênh của bạn đến từ một tệp XMLTV, hãy sử dụng lớp XmlTvParser. Nếu không, hãy tạo các kênh theo phương thức lập trình bằng cách sử dụng lớp Channel.Builder.

Đối với mỗi kênh, hệ thống sẽ gọi getProgramsForChannel khi cần danh sách các chương trình có thể xem trong một khoảng thời gian nhất định trên kênh đó. Trả về một danh sách các đối tượng Program cho kênh. Sử dụng lớp XmlTvParser để lấy các chương trình từ tệp XMLTV hoặc tạo chúng theo chương trình bằng cách sử dụng lớp Program.Builder.

Đối với mỗi đối tượng Program, hãy sử dụng một đối tượng InternalProviderData để đặt thông tin chương trình, chẳng hạn như loại video của chương trình. Nếu bạn chỉ có một số lượng chương trình hạn chế mà bạn muốn kênh phát lại liên tục, hãy sử dụng phương thức InternalProviderData.setRepeatable với giá trị true khi thiết lập thông tin về chương trình của bạn.

Sau khi triển khai dịch vụ công việc, hãy thêm nó vào tệp kê khai ứng dụng của bạn:

<service
    android:name=".sync.SampleJobService"
    android:permission="android.permission.BIND_JOB_SERVICE"
    android:exported="true" />

Cuối cùng, hãy tạo một hoạt động thiết lập. Hoạt động thiết lập của bạn phải cung cấp một cách để đồng bộ hoá dữ liệu kênh và chương trình. Một cách để thực hiện điều này là người dùng có thể sử dụng giao diện người dùng trong hoạt động đó. Bạn cũng có thể để ứng dụng tự động thực hiện việc này khi hoạt động bắt đầu. Khi hoạt động thiết lập cần đồng bộ hoá thông tin về kênh và chương trình, ứng dụng sẽ bắt đầu dịch vụ công việc:

Kotlin

val inputId = getActivity().intent.getStringExtra(TvInputInfo.EXTRA_INPUT_ID)
EpgSyncJobService.cancelAllSyncRequests(getActivity())
EpgSyncJobService.requestImmediateSync(
        getActivity(),
        inputId,
        ComponentName(getActivity(), SampleJobService::class.java)
)

Java

String inputId = getActivity().getIntent().getStringExtra(TvInputInfo.EXTRA_INPUT_ID);
EpgSyncJobService.cancelAllSyncRequests(getActivity());
EpgSyncJobService.requestImmediateSync(getActivity(), inputId,
        new ComponentName(getActivity(), SampleJobService.class));

Sử dụng phương thức requestImmediateSync để đồng bộ hóa dịch vụ công việc. Người dùng phải đợi quá trình đồng bộ hoá hoàn tất, vì vậy, bạn nên giữ khoảng thời gian yêu cầu tương đối ngắn.

Sử dụng phương thức setUpPeriodicSync để dịch vụ công việc định kỳ đồng bộ hóa dữ liệu kênh và chương trình trong nền:

Kotlin

EpgSyncJobService.setUpPeriodicSync(
        context,
        inputId,
        ComponentName(context, SampleJobService::class.java)
)

Java

EpgSyncJobService.setUpPeriodicSync(context, inputId,
        new ComponentName(context, SampleJobService.class));

Thư viện TIF Companion cung cấp thêm một phương thức quá tải của requestImmediateSync cho phép bạn chỉ định thời lượng dữ liệu kênh cần đồng bộ hóa tính bằng mili giây. Phương thức mặc định sẽ đồng bộ hoá dữ liệu kênh trong một giờ.

Thư viện TIF Companion cũng cung cấp một phương thức nạp chồng bổ sung của setUpPeriodicSync cho phép bạn chỉ định thời lượng đồng bộ hoá dữ liệu kênh và tần suất đồng bộ hoá định kỳ. Phương pháp mặc định đồng bộ dữ liệu kênh trong 48 giờ mỗi 12 giờ.

Để biết thêm thông tin chi tiết về dữ liệu kênh và EPG, hãy xem phần Làm việc với dữ liệu kênh.

Xử lý các yêu cầu điều chỉnh và phát lại phương tiện

Khi người dùng chọn một kênh cụ thể, ứng dụng truyền hình của hệ thống sẽ sử dụng một Session, được tạo bởi ứng dụng của bạn, để dò đến kênh được yêu cầu và phát nội dung. Thư viện TIF Companion cung cấp một số lớp mà bạn có thể mở rộng để xử lý các lệnh gọi kênh và phiên từ hệ thống.

Lớp con BaseTvInputService của bạn tạo ra các phiên xử lý yêu cầu điều chỉnh. Ghi đè phương thức onCreateSession, tạo một phiên được mở rộng từ lớp BaseTvInputService.Session và gọi super.sessionCreated bằng phiên mới của bạn. Trong ví dụ sau, onCreateSession trả về một đối tượng RichTvInputSessionImpl mở rộng BaseTvInputService.Session:

Kotlin

override fun onCreateSession(inputId: String): Session =
        RichTvInputSessionImpl(this, inputId).apply {
            setOverlayViewEnabled(true)
        }

Java

@Override
public final Session onCreateSession(String inputId) {
    RichTvInputSessionImpl session = new RichTvInputSessionImpl(this, inputId);
    session.setOverlayViewEnabled(true);
    return session;
}

Khi người dùng sử dụng ứng dụng truyền hình của hệ thống để bắt đầu xem một trong các kênh của bạn, hệ thống sẽ gọi phương thức onPlayChannel của phiên của bạn. Ghi đè phương thức này nếu bạn cần thực hiện bất kỳ hoạt động khởi tạo kênh đặc biệt nào trước khi chương trình bắt đầu phát.

Sau đó, hệ thống sẽ lấy chương trình hiện đang được lên lịch và gọi phương thức onPlayProgram của phiên của bạn, chỉ định thông tin chương trình và thời gian bắt đầu tính bằng mili giây. Sử dụng giao diện TvPlayer để bắt đầu phát chương trình.

Mã trình phát đa phương tiện của bạn phải triển khai TvPlayer để xử lý các sự kiện phát cụ thể. Lớp TvPlayer xử lý các tính năng như chế độ điều khiển tua đi tua lại mà không làm tăng độ phức tạp cho quá trình triển khai BaseTvInputService.

Trong phương thức getTvPlayer của phiên, hãy trả về trình phát nội dung đa phương tiện triển khai TvPlayer. Ứng dụng mẫu Dịch vụ đầu vào TV triển khai trình phát đa phương tiện sử dụng ExoPlayer.

Tạo dịch vụ đầu vào TV bằng cách sử dụng khung đầu vào TV.

Nếu dịch vụ đầu vào của TV không thể sử dụng Thư viện đồng hành TIF, bạn cần triển khai các thành phần sau:

  • TvInputService cung cấp khả năng hoạt động liên tục và ngầm định cho đầu vào TV
  • TvInputService.Session duy trì trạng thái đầu vào của TV và giao tiếp với ứng dụng lưu trữ
  • TvContract mô tả các kênh và chương trình có sẵn cho đầu vào TV
  • TvContract.Channels biểu thị thông tin về một kênh truyền hình
  • TvContract.Programs mô tả một chương trình truyền hình với các dữ liệu như tiêu đề chương trình và thời gian bắt đầu.
  • TvTrackInfo biểu thị một bản âm thanh, video hoặc phụ đề
  • TvContentRating mô tả mức phân loại nội dung, cho phép các lược đồ phân loại nội dung tùy chỉnh
  • TvInputManager cung cấp một API cho ứng dụng truyền hình hệ thống và quản lý hoạt động tương tác với các đầu vào và ứng dụng TV

Bạn cũng cần làm như sau:

  1. Khai báo dịch vụ đầu vào TV của bạn trong tệp kê khai, như được mô tả trong Khai báo dịch vụ đầu vào TV của bạn trong tệp kê khai.
  2. Tạo tệp siêu dữ liệu dịch vụ.
  3. Tạo và đăng ký thông tin về kênh và chương trình.
  4. Tạo hoạt động thiết lập.

Xác định dịch vụ đầu vào TV

Đối với dịch vụ của bạn, hãy mở rộng lớp TvInputService. Một triển khai TvInputService là một dịch vụ ràng buộc trong đó dịch vụ hệ thống là máy khách ràng buộc với nó. Các phương thức vòng đời dịch vụ mà bạn cần triển khai được minh hoạ trong hình 1.

Phương thức onCreate khởi tạo và bắt đầu HandlerThread. Phương thức này cung cấp một luồng xử lý riêng biệt với luồng giao diện người dùng để xử lý các thao tác do hệ thống điều khiển. Trong ví dụ sau, phương thức onCreate sẽ khởi chạy CaptioningManager và chuẩn bị xử lý các thao tác ACTION_BLOCKED_RATINGS_CHANGEDACTION_PARENTAL_CONTROLS_ENABLED_CHANGED. Các thao tác này mô tả ý định của hệ thống được kích hoạt khi người dùng thay đổi chế độ cài đặt kiểm soát của cha mẹ và khi có thay đổi về danh sách phân loại bị chặn.

Kotlin

override fun onCreate() {
    super.onCreate()
    handlerThread = HandlerThread(javaClass.simpleName).apply {
        start()
    }
    dbHandler = Handler(handlerThread.looper)
    handler = Handler()
    captioningManager = getSystemService(Context.CAPTIONING_SERVICE) as CaptioningManager

    setTheme(android.R.style.Theme_Holo_Light_NoActionBar)

    sessions = mutableListOf<BaseTvInputSessionImpl>()
    val intentFilter = IntentFilter().apply {
        addAction(TvInputManager.ACTION_BLOCKED_RATINGS_CHANGED)
        addAction(TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED)
    }
    registerReceiver(broadcastReceiver, intentFilter)
}

Java

@Override
public void onCreate() {
    super.onCreate();
    handlerThread = new HandlerThread(getClass()
      .getSimpleName());
    handlerThread.start();
    dbHandler = new Handler(handlerThread.getLooper());
    handler = new Handler();
    captioningManager = (CaptioningManager)
      getSystemService(Context.CAPTIONING_SERVICE);

    setTheme(android.R.style.Theme_Holo_Light_NoActionBar);

    sessions = new ArrayList<BaseTvInputSessionImpl>();
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(TvInputManager
      .ACTION_BLOCKED_RATINGS_CHANGED);
    intentFilter.addAction(TvInputManager
      .ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED);
    registerReceiver(broadcastReceiver, intentFilter);
}

Hình 1.Vòng đời TvInputService.

Xem Kiểm soát nội dung để biết thêm thông tin về cách làm việc với nội dung bị chặn và cung cấp tính năng kiểm soát của phụ huynh. Hãy xem TvInputManager để biết thêm các thao tác do hệ thống điều khiển mà bạn có thể muốn xử lý trong dịch vụ đầu vào của TV.

TvInputService tạo ra một TvInputService.Session thực hiện Handler.Callback để xử lý các thay đổi trạng thái của người chơi. Với onSetSurface, TvInputService.Session sẽ đặt Surface bằng nội dung video. Xem Tích hợp trình phát với bề mặt để biết thêm thông tin về cách làm việc với Surface để hiển thị video.

TvInputService.Session xử lý sự kiện onTune khi người dùng chọn một kênh, đồng thời thông báo cho ứng dụng truyền hình hệ thống về những thay đổi trong nội dung và siêu dữ liệu nội dung. Các phương thức notify này được mô tả trong phần Kiểm soát nội dungXử lý lựa chọn về bản phụ đề trong khoá huấn luyện này.

Xác định hoạt động thiết lập

Ứng dụng truyền hình hệ thống hoạt động với hoạt động thiết lập mà bạn xác định cho đầu vào TV. Bạn phải thiết lập hoạt động và cung cấp ít nhất một bản ghi kênh cho cơ sở dữ liệu hệ thống. Ứng dụng truyền hình của hệ thống sẽ kích hoạt hoạt động thiết lập khi không tìm thấy kênh cho đầu vào TV.

Hoạt động thiết lập mô tả cho ứng dụng truyền hình hệ thống các kênh được cung cấp thông qua đầu vào TV, như minh hoạ trong bài học tiếp theo: Tạo và cập nhật dữ liệu kênh.

Tài liệu tham khảo bổ sung