TV에서 멀티태스킹하기

Android 14 (API 수준 34)에서는 멀티태스킹을 허용하기 위해 PIP 모드 (PiP) API에 몇 가지 개선사항을 도입했습니다. PiP 지원은 Android 8.0 (API 수준 26)에서 도입되었지만 Android TV에서는 널리 지원되지 않았으며 Android 13 이전의 Google TV에서는 전혀 지원되지 않았습니다. TV용 멀티태스킹은 PIP 모드를 사용하여 화면에 두 개의 개별 앱이 공존할 수 있도록 합니다. 하나는 전체 화면으로 실행되고 다른 하나는 PIP 모드로 실행됩니다. 이러한 모드 중 하나에서 실행되는 앱에는 다양한 요구사항이 있습니다.

기본 동작은 PIP 앱이 전체 화면 앱을 오버레이하는 것입니다. 이는 표준 Android PIP 모드 동작과 거의 동일합니다.

멀티태스킹을 통합할 때 애플리케이션은 TV 앱 품질 가이드라인에 따라 사용 유형을 선언해야 합니다.

PIP 모드로 앱 실행

Android 14 (API 수준 34) 이상을 실행하는 TV 기기의 경우 enterPictureInPictureMode()를 호출하여 PiP 모드로 앱을 실행합니다. 이전 버전의 Android를 실행하는 TV 기기는 PIP 모드를 지원하지 않습니다.

다음은 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
        }
}

자바

@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);
    }
}

이 작업은 기기에 시스템 기능 FEATURE_PICTURE_IN_PICTURE이 있는 경우에만 추가됩니다. 또한 작업이 트리거되면 PIP 모드의 가로세로 비율이 재생 중인 동영상의 가로세로 비율과 일치하도록 설정됩니다.

제목과 부제목을 추가하여 이 PIP가 일반적으로 사용되는 용도에 관한 사용자 정보를 제공해야 합니다.

PIP 모드로 실행되는 앱과 공존

앱이 전체 화면 앱으로 실행 중인 경우 PIP 모드로 실행 중인 다른 앱에 맞게 조정해야 할 수 있습니다.

Keep-clear API

경우에 따라 PiP 앱이 전체 화면 앱 내에서 중요한 UI 구성요소를 오버레이할 수 있습니다. 이를 완화하기 위해 앱에서 오버레이해서는 안 되는 중요한 UI 구성요소를 식별하는 데 사용할 수 있는 keep-clear API가 있습니다. 시스템은 PiP 창의 위치를 변경하여 이러한 구성요소를 가리지 않도록 요청을 처리하려고 시도합니다.

중요한 UI 구성요소를 가리지 않도록 PiP 창 배치
그림 1. 중요한 UI 구성요소를 가리지 않도록 PiP 창 배치

뷰가 오버레이되지 않도록 지정하려면 다음 예와 같이 XML 레이아웃에서 preferKeepClear를 사용합니다.

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

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
}

자바

private MyLayoutBinding binding;

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

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

전체 View를 지울 필요는 없고 일부만 지워야 하는 경우도 있습니다. setPreferKeepClearRects()를 사용하여 오버레이해서는 안 되는 View의 영역을 지정할 수 있습니다. Flutter, Jetpack Compose, WebView와 같이 View를 기본적으로 사용하지 않는 UI에는 영역을 비워 두어야 하는 하위 섹션이 있을 수 있습니다. 이 API는 이러한 경우에 사용할 수 있습니다.

사용량 유형

앱은 PIP 모드의 기본 사용 유형에 해당하는 com.google.android.tv.pip.category의 메타데이터 값 속성을 선언해야 합니다. android:supportsPictureInPicture="true"를 설정한 모든 <activity>는 다음 표의 관련 값으로 이 속성을 선언해야 합니다.

이러한 카테고리에 속하지 않는 사용 유형, 특히 미디어 콘텐츠 재생은 TV의 PIP 모드에서 허용되지 않습니다.

값 설명
"communication" 영상 통화 또는 음성 통화와 같은 커뮤니케이션 사용 사례
"smartHome" 연결된 초인종 또는 베이비 모니터와 같은 스마트 홈 통합
"health" 피트니스 추적 또는 건강 모니터링과 같은 건강 사용 사례
"ticker" 실시간 스포츠 점수, 뉴스, 주식 티커와 같은 티커 사용 사례

여러 값은 세로 막대 (|)로 구분됩니다. 예를 들면 다음과 같습니다.

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