Làm nhiều việc cùng lúc trên TV

Android 14 (API cấp 34) giới thiệu một số tính năng nâng cao cho các API Hình trong hình (PiP) để cho phép đa nhiệm. Mặc dù đã ra mắt tính năng hỗ trợ PiP trong Android 8.0 (API cấp 26), nhưng tính năng này không được hỗ trợ rộng rãi trên Android TV và hoàn toàn không được hỗ trợ trên Google TV trước Android 13. Tính năng đa nhiệm cho TV sử dụng chế độ PiP để cho phép 2 ứng dụng riêng biệt cùng tồn tại trên màn hình: một ứng dụng chạy ở chế độ toàn màn hình, ứng dụng thứ hai chạy ở chế độ PiP. Ứng dụng chạy ở một trong hai chế độ này có các yêu cầu khác nhau.

Hành vi mặc định là ứng dụng PiP phủ lên ứng dụng toàn màn hình. Hành vi này giống nhiều với hành vi hình trong hình của Android tiêu chuẩn.

Xin lưu ý rằng khi tích hợp tính năng đa nhiệm, ứng dụng của bạn phải khai báo các kiểu sử dụng theo theo nguyên tắc về chất lượng của ứng dụng dành cho TV.

Chạy ứng dụng ở chế độ PiP (Hình trong hình)

Đối với các thiết bị TV chạy Android 14 (API cấp 34) trở lên, hãy chạy ứng dụng của bạn ở chế độ Hình trong hình bằng cách gọi enterPictureInPictureMode(). Các thiết bị TV chạy các phiên bản Android trước đây không hỗ trợ chế độ Hình trong hình.

Dưới đây là ví dụ về cách triển khai logic của một nút để chuyển sang chế độ PiP:

Kotlin

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    pictureInPictureButton.visibility =
        if (requireActivity().packageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
            pictureInPictureButton.setOnClickListener {
                val aspectRatio = Rational(view.width, view.height)
                val params = PictureInPictureParams.Builder()
                    .setAspectRatio(aspectRatio)
                    .build()
                val result = requireActivity().enterPictureInPictureMode(params)
            }
            View.VISIBLE
        } else {
            View.GONE
        }
}

Java

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    if (requireActivity().getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
        pictureInPictureButton.setVisibility(View.VISIBLE);
        pictureInPictureButton.setOnClickListener(v -> {
            Rational aspectRatio = new Rational(view.getWidth(), view.getHeight());
            PictureInPictureParams params = new PictureInPictureParams.Builder()
                    .setAspectRatio(aspectRatio)
                    .setTitle("My Streaming App")
                    .setSubtitle("My On-Demand Content")
                    .build();
            Boolean result = requireActivity().enterPictureInPictureMode(params);
        });
    } else {
        pictureInPictureButton.setVisibility(View.GONE);
    }
}

Hành động này chỉ được thêm vào nếu thiết bị có tính năng hệ thống FEATURE_PICTURE_IN_PICTURE. Ngoài ra, khi hành động được kích hoạt, tỷ lệ khung hình của chế độ PiP được thiết lập để khớp với tỷ lệ khung hình của video đang phát.

Hãy nhớ thêm tiêu đềphụ đề để cung cấp cho người dùng thông tin về mục đích sử dụng thường xuyên của PIP này.

Cùng tồn tại với các ứng dụng chạy ở chế độ Hình trong hình

Khi đang chạy dưới dạng ứng dụng toàn màn hình, ứng dụng đó có thể cần phải điều chỉnh cho phù hợp với các ứng dụng khác chạy ở chế độ PiP.

API Keep-clear

Trong một số trường hợp, ứng dụng PiP có thể phủ các thành phần giao diện người dùng quan trọng trong ứng dụng toàn màn hình. Để giảm thiểu tình trạng này, ứng dụng có thể dùng các API rõ ràng để xác định các thành phần giao diện người dùng quan trọng không nên phủ lên. Hệ thống cố gắng tuân thủ các yêu cầu để tránh che khuất các thành phần này bằng cách đặt lại vị trí cửa sổ PiP.

Xóa

Để chỉ định rằng một thành phần hiển thị không được phủ lên trên, hãy sử dụng preferKeepClear trong bố cục XML như trong ví dụ sau:

<TextView
    android:id="@+id/important_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:preferKeepClear="true"
    android:text="@string/app_name"/>

Bạn cũng có thể thực hiện việc này theo phương thức lập trình bằng setPreferKeepClear():

Kotlin

private lateinit var binding: MyLayoutBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    binding = MyLayoutBinding.inflate(layoutInflater)
    setContentView(binding.root)
    binding.importantText.isPreferKeepClear = true
}

Java

private MyLayoutBinding binding;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    binding = MyLayoutBinding.inflate(getLayoutInflater());
    setContentView(binding.getRoot());
    binding.importantText.setPreferKeepClear(true);
}

Đôi khi, có thể bạn không cần giữ rõ toàn bộ View mà chỉ cần giữ lại một phần. Bạn có thể dùng setPreferKeepClearRects() để chỉ định các vùng của View không được phủ lên. Những giao diện người dùng không dùng View vốn có, chẳng hạn như Flutter, Jetpack Compose và WebView, có thể có các phần phụ cần xoá các vùng. Bạn có thể sử dụng API này cho những trường hợp đó.

Kiểu sử dụng

Ứng dụng của bạn phải khai báo thuộc tính giá trị siêu dữ liệu của com.google.android.tv.pip.category tương ứng với kiểu sử dụng chính hoặc kiểu sử dụng cho chế độ hình trong hình. Mọi <activity> đã đặt android:supportsPictureInPicture="true" đều phải khai báo thuộc tính này với giá trị liên quan trong bảng bên dưới.

Các kiểu sử dụng không thuộc bất kỳ danh mục nào trong số này, cụ thể là mọi hoạt động phát nội dung nghe nhìn, đều không được phép ở chế độ hình trong hình trên TV.

Giá trị Nội dung mô tả
"communication" Các trường hợp sử dụng tính năng giao tiếp, chẳng hạn như cuộc gọi video hoặc cuộc gọi thoại.
"smartHome" Các thiết bị tích hợp nhà thông minh, chẳng hạn như chuông cửa đã kết nối hoặc thiết bị giám sát em bé.
"health" Các trường hợp sử dụng liên quan đến sức khoẻ, chẳng hạn như theo dõi hoạt động thể dục hoặc theo dõi sức khoẻ.
"ticker" Các trường hợp sử dụng tin tức, chẳng hạn như tỷ số thể thao trực tiếp hoặc tin tức và mã cổ phiếu.

Nhiều giá trị được phân tách bằng dấu gạch đứng (|). Ví dụ:

<meta-data android:name="com.google.android.tv.pip.category" android:value="smartHome|health" />