홈 화면 채널

Android TV 홈 화면 또는 간단히 홈 화면은 추천 콘텐츠를 채널프로그램의 표로 표시하는 UI를 제공합니다. 각 행이 채널입니다. 채널에는 이 채널에서 볼 수 있는 모든 프로그램의 카드가 포함되어 있습니다.

TV 홈 화면

이 페이지에서는 채널과 프로그램을 홈 화면에 추가하고, 콘텐츠를 업데이트하고, 사용자 작업을 처리하고, 사용자에게 최고의 환경을 제공하는 방법을 보여줍니다. (API를 자세히 살펴보려면 홈 화면 codelab을 사용해 보고 I/O 2017 Android TV 세션을 시청하세요.)

참고: 추천 채널은 Android 8.0(API 레벨 26) 이상에서만 사용할 수 있습니다. Android 8.0(API 레벨 26) 이상에서 실행되는 앱을 위한 추천 항목을 제공하려면 추천 채널을 사용해야 합니다. 이전 버전의 Android에서 실행되는 앱을 위한 추천 항목을 제공하려면 대신 앱에서 추천 행을 사용해야 합니다.

홈 화면 UI

앱은 새 채널을 만들고 프로그램을 채널에 추가, 삭제 및 업데이트하며 채널의 프로그램 순서를 제어할 수 있습니다. 예를 들어, 앱에서 '새로운 기능'이라는 채널을 만들고 새로 공개된 프로그램의 카드를 표시할 수 있습니다.

앱은 채널이 홈 화면에 나타나는 순서를 제어할 수 없습니다. 앱에서 새 채널을 만들면 이 채널이 홈 화면에서 채널 목록의 하단에 추가됩니다. 사용자가 채널을 재정렬하고 숨기고 표시할 수 있습니다.

Watch Next 채널

Watch Next 채널은 홈 화면에서 앱 행 다음에 나타나는 두 번째 행입니다. 시스템에서 이 채널을 만들고 유지합니다. 앱에서 Watch Next 채널에 프로그램을 추가할 수 있습니다. 사용자가 관심 항목으로 표시하거나 중간에 시청을 중단한 프로그램 또는 사용자가 시청하고 있는 콘텐츠와 관련된 프로그램(예: 시리즈의 다음 에피소드 또는 프로그램의 다음 시즌)을 추가합니다.

Watch Next 채널에는 몇 가지 제약이 있습니다. 앱에서 Watch Next 채널의 행을 이동하거나 삭제하거나 숨길 수 없습니다.

앱 채널

앱에서 만드는 채널은 모두 다음 수명 주기를 따릅니다.

  1. 사용자가 앱에서 채널을 탐색하여 홈 화면에 추가하도록 요청합니다.
  2. 앱에서 채널을 만들고 TvProvider에 추가합니다(이 시점에는 이 채널이 표시되지 않음).
  3. 앱이 시스템에 이 채널을 표시하도록 요청합니다.
  4. 시스템이 사용자에게 새 채널을 승인하도록 요청합니다.
  5. 새 채널이 홈 화면의 마지막 행에 표시됩니다.

기본 채널

앱은 사용자가 홈 화면에 추가하는 여러 채널을 제공할 수 있습니다. 일반적으로 채널이 홈 화면에 표시되기 전에 사용자가 각 채널을 선택하고 승인해야 합니다. 모든 앱에는 하나의 기본 채널을 만드는 옵션이 있습니다. 기본 채널은 자동으로 홈 화면에 표시되므로 특수하며, 사용자가 명시적으로 요청할 필요가 없습니다.

기본 요건

Android TV 홈 화면에서는 Android의 TvProvider API를 사용하여 앱에서 만드는 채널과 프로그램을 관리합니다. 제공자의 데이터에 액세스하려면 앱의 manifest에 다음 권한을 추가하세요.

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

TvProvider 지원 라이브러리를 통해 제공자를 쉽게 사용할 수 있습니다. 이 라이브러리를 build.gradle 파일의 종속성에 추가하세요.

compile 'com.android.support:support-tv-provider:27.0.0'
    

채널 및 프로그램 작업을 실행하려면 이러한 지원 라이브러리 가져오기를 프로그램에 포함해야 합니다.

Kotlin

    import android.support.media.tv.Channel
    import android.support.media.tv.TvContractCompat
    import android.support.media.tv.ChannelLogoUtils
    import android.support.media.tv.PreviewProgram
    import android.support.media.tv.WatchNextProgram
    

자바

    import android.support.media.tv.Channel;
    import android.support.media.tv.TvContractCompat;
    import android.support.media.tv.ChannelLogoUtils;
    import android.support.media.tv.PreviewProgram;
    import android.support.media.tv.WatchNextProgram;
    

채널

앱에서 만든 첫 번째 채널이 기본 채널이 됩니다. 기본 채널은 홈 화면에 자동으로 표시됩니다. 사용자가 만든 다른 모든 채널은 사용자가 선택하고 승인해야 홈 화면에 표시될 수 있습니다.

채널 만들기

앱이 포그라운드에서 실행되는 경우에만 새로 추가된 채널을 표시하도록 시스템에 요청해야 합니다. 이렇게 하면 사용자가 다른 앱을 실행하는 동안 앱에서 채널 추가 승인을 요청하는 대화상자를 표시하지 않습니다. 백그라운드에서 실행하는 동안 채널을 추가하려고 하면 활동의 onActivityResult() 메서드가 상태 코드 RESULT_CANCELED를 반환합니다.

채널을 만들려면 다음 단계를 따르세요.

  1. 채널 빌더를 만들고 속성을 설정합니다. 채널 유형은 TYPE_PREVIEW여야 합니다. 필요에 따라 속성을 더 추가합니다.

    Kotlin

        val builder = Channel.Builder()
        // Every channel you create must have the type TYPE_PREVIEW
        builder.setType(TvContractCompat.Channels.TYPE_PREVIEW)
                .setDisplayName("Channel Name")
                .setAppLinkIntentUri(uri)
        

    자바

        Channel.Builder builder = new Channel.Builder();
        // Every channel you create must have the type TYPE_PREVIEW
        builder.setType(TvContractCompat.Channels.TYPE_PREVIEW)
                .setDisplayName("Channel Name")
                .setAppLinkIntentUri(uri);
        
  2. 채널을 제공자에 삽입합니다.

    Kotlin

        var channelUri = context.contentResolver.insert(
                TvContractCompat.Channels.CONTENT_URI, builder.build().toContentValues())
        

    자바

        Uri channelUri = context.getContentResolver().insert(
                TvContractCompat.Channels.CONTENT_URI, builder.build().toContentValues());
        
  3. 나중에 프로그램을 채널에 추가하기 위해 채널 ID를 저장해야 합니다. 반환된 URI에서 채널 ID를 추출합니다.

    Kotlin

        var channelId = ContentUris.parseId(channelUri)
        

    자바

        long channelId = ContentUris.parseId(channelUri);
        
  4. 채널의 로고를 추가해야 합니다. Uri 또는 Bitmap을 사용하세요. 로고 아이콘은 80dp x 80dp이고 불투명해야 합니다. 로고 아이콘은 원형 마스크 아래에 표시됩니다.

    TV 홈 화면 아이콘 마스크

    Kotlin

        // Choose one or the other
        storeChannelLogo(context: Context, channelId: Long, logoUri: Uri) // also works if logoUri is a URL
        storeChannelLogo(context: Context, channelId: Long, logo: Bitmap)
        

    자바

        // Choose one or the other
        storeChannelLogo(Context context, long channelId, Uri logoUri); // also works if logoUri is a URL
        storeChannelLogo(Context context, long channelId, Bitmap logo);
        
  5. 기본 채널을 만듭니다(선택사항). 앱에서 첫 번째 채널을 만들 때 사용자 작업 없이 즉시 홈 화면에 표시되도록 이 채널을 기본 채널로 지정할 수 있습니다. 사용자가 만든 다른 모든 채널은 사용자가 명시적으로 선택할 때까지 표시되지 않습니다.

    Kotlin

        TvContractCompat.requestChannelBrowsable(context, channelId)
        

    자바

        TvContractCompat.requestChannelBrowsable(context, channelId);
        

  6. 앱을 열기 전에 기본 채널이 표시되도록 합니다. 앱이 설치된 후 홈 화면에서 보내는 android.media.tv.action.INITIALIZE_PROGRAMS 작업을 수신 대기하는 BroadcastReceiver를 추가하여 이 동작을 발생시킬 수 있습니다.
        <receiver
          android:name=".RunOnInstallReceiver"
          android:exported="true">
            <intent-filter>
              <action android:name="android.media.tv.action.INITIALIZE_PROGRAMS" />
              <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>
        
    개발 중에 앱을 사이드로드할 때 adb를 통해 인텐트를 트리거하여 이 단계를 테스트할 수 있습니다. 여기서 your.package.name/.YourReceiverName은 앱의 BroadcastReceiver입니다.

        adb shell am broadcast -a android.media.tv.action.INITIALIZE_PROGRAMS -n \
            your.package.name/.YourReceiverName
        

    드문 경우이지만 사용자가 앱을 시작하는 동시에 앱이 브로드캐스트를 수신할 수 있습니다. 코드가 기본 채널을 두 번 이상 추가하지 않도록 하세요.

채널 업데이트

채널 업데이트는 채널을 만드는 것과 매우 유사합니다.

다른 Channel.Builder를 사용하여 변경해야 하는 속성을 설정하세요.

ContentResolver를 사용하여 채널을 업데이트하세요. 채널이 처음에 추가될 때 저장한 채널 ID를 사용하세요.

Kotlin

    context.contentResolver.update(
            TvContractCompat.buildChannelUri(channelId),
            builder.build().toContentValues(),
            null,
            null
    )
    

자바

    context.getContentResolver().update(TvContractCompat.buildChannelUri(channelId),
        builder.build().toContentValues(), null, null);
    

채널의 로고를 업데이트하려면 storeChannelLogo()를 사용하세요.

채널 삭제

Kotlin

    context.contentResolver.delete(TvContractCompat.buildChannelUri(channelId), null, null)
    

자바

    context.getContentResolver().delete(TvContractCompat.buildChannelUri(channelId), null, null);
    

프로그램

앱 채널에 프로그램 추가

PreviewProgram.Builder를 만들고 속성을 설정하세요.

Kotlin

    val builder = PreviewProgram.Builder()
    builder.setChannelId(channelId)
            .setType(TvContractCompat.PreviewPrograms.TYPE_CLIP)
            .setTitle("Title")
            .setDescription("Program description")
            .setPosterArtUri(uri)
            .setIntentUri(uri)
            .setInternalProviderId(appProgramId)
    

자바

    PreviewProgram.Builder builder = new PreviewProgram.Builder();
    builder.setChannelId(channelId)
            .setType(TvContractCompat.PreviewPrograms.TYPE_CLIP)
            .setTitle("Title")
            .setDescription("Program description")
            .setPosterArtUri(uri)
            .setIntentUri(uri)
            .setInternalProviderId(appProgramId);
    

프로그램의 유형에 따라 속성을 더 추가하세요. (각 프로그램 유형에 사용 가능한 속성을 보려면 아래 를 참조하세요.)

프로그램을 제공자에 삽입하세요.

Kotlin

    var programUri = context.contentResolver.insert(TvContractCompat.PreviewPrograms.CONTENT_URI,
            builder.build().toContentValues())
    

자바

    Uri programUri = context.getContentResolver().insert(TvContractCompat.PreviewPrograms.CONTENT_URI,
          builder.build().toContentValues());
    

나중에 참조할 수 있도록 프로그램 ID를 검색하세요.

Kotlin

    val programId = ContentUris.parseId(programUri)
    

자바

    long programId = ContentUris.parseId(programUri);
    

Watch Next 채널에 프로그램 추가

Watch Next 채널에 프로그램을 삽입하는 과정은 자체 채널에 프로그램을 삽입하는 과정과 동일합니다.

네 가지 유형의 프로그램이 있습니다. 적절한 유형을 선택하세요.

유형메모
WATCH_NEXT_TYPE_CONTINUE사용자가 콘텐츠 시청을 중지했습니다.
WATCH_NEXT_TYPE_NEXT사용자가 시청 중인 시리즈의 이용 가능한 다음 프로그램을 시청할 수 있습니다. 예를 들어, 사용자가 시리즈의 에피소드 3을 시청하고 있는 경우 앱에서 다음으로 에피소드 4를 시청하도록 제안할 수 있습니다.
WATCH_NEXT_TYPE_NEW사용자가 시청하고 있는 내용을 명확하게 따르는 새 콘텐츠를 시청할 수 있습니다. 예를 들어, 사용자가 시리즈의 에피소드 번호 5를 시청 중이며 에피소드 6을 시청할 수 있습니다.
WATCH_NEXT_TYPE_WATCHLIST사용자가 프로그램을 저장할 때 시스템에서 앱에서 삽입했습니다.

WatchNextProgram.Builder를 사용하세요.

Kotlin

    val builder = WatchNextProgram.Builder()
    builder.setType(TvContractCompat.WatchNextPrograms.TYPE_CLIP)
            .setWatchNextType(TvContractCompat.WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
            .setLastEngagementTimeUtcMillis(time)
            .setTitle("Title")
            .setDescription("Program description")
            .setPosterArtUri(uri)
            .setIntentUri(uri)
            .setInternalProviderId(appProgramId)

    val watchNextProgramUri = context.contentResolver
            .insert(TvContractCompat.WatchNextPrograms.CONTENT_URI,
                    builder.build().toContentValues())
    

자바

    WatchNextProgram.Builder builder = new WatchNextProgram.Builder();
    builder.setType(TvContractCompat.WatchNextPrograms.TYPE_CLIP)
            .setWatchNextType(TvContractCompat.WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
            .setLastEngagementTimeUtcMillis(time)
            .setTitle("Title")
            .setDescription("Program description")
            .setPosterArtUri(uri)
            .setIntentUri(uri)
            .setInternalProviderId(appProgramId);

    Uri watchNextProgramUri = context.getContentResolver()
            .insert(TvContractCompat.WatchNextPrograms.CONTENT_URI, builder.build().toContentValues());
    

Watch Next 프로그램을 업데이트하는 데 필요한 Uri를 만들려면 TvContractCompat.buildWatchNextProgramUri(long watchNextProgramId)를 사용하세요.

사용자가 프로그램을 Watch Next 채널에 추가할 때 시스템에서 프로그램을 행에 복사합니다. 앱에 프로그램이 추가되었음을 알리기 위해 TvContractCompat.ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT 인텐트를 보냅니다. 이 인텐트에는 두 개의 엑스트라, 즉 복사된 프로그램 ID와 Watch Next 채널의 프로그램을 위해 생성된 프로그램 ID가 포함됩니다.

프로그램 업데이트

프로그램 정보를 변경할 수 있습니다. 예를 들어, 영화의 대여료를 업데이트하거나 사용자가 시청한 프로그램의 양을 표시하는 진행률 표시줄을 업데이트할 수 있습니다.

PreviewProgram.Builder를 사용하여 변경해야 하는 속성을 설정한 다음 getContentResolver().update를 호출하여 프로그램을 업데이트하세요. 처음에 프로그램을 추가할 때 저장한 프로그램 ID를 지정하세요.

Kotlin

    context.contentResolver.update(
            TvContractCompat.buildPreviewProgramUri(programId),
                    builder.build().toContentValues(), null, null
    )
    

자바

    context.getContentResolver().update(TvContractCompat.buildPreviewProgramUri(programId),
        builder.build().toContentValues(), null, null);
    

프로그램 삭제

Kotlin

    context.contentResolver
            .delete(TvContractCompat.buildPreviewProgramUri(programId), null, null)
    

자바

    context.getContentResolver().delete(TvContractCompat.buildPreviewProgramUri(programId), null, null);
    

사용자 작업 처리

앱은 채널을 표시하고 추가할 수 있는 UI를 제공하여 사용자가 콘텐츠를 탐색하는 데 도움을 줄 수 있습니다. 또한 채널이 홈 화면에 표시된 후 앱이 채널과의 상호작용을 처리해야 합니다.

채널 탐색 및 추가

앱은 사용자가 채널을 선택하고 추가할 수 있는 UI 요소를 제공할 수 있습니다(예: 채널을 추가하도록 요청하는 버튼).

사용자가 특정 채널을 요청한 후 다음 코드를 실행하여 채널을 홈 화면 UI에 추가할 수 있는 사용자의 권한을 얻으세요.

Kotlin

    val intent = Intent(TvContractCompat.ACTION_REQUEST_CHANNEL_BROWSABLE)
    intent.putExtra(TvContractCompat.EXTRA_CHANNEL_ID, channelId)
    try {
      activity.startActivityForResult(intent, 0)
    } catch (e: ActivityNotFoundException) {
      // handle error
    }
    

자바

    Intent intent = new Intent(TvContractCompat.ACTION_REQUEST_CHANNEL_BROWSABLE);
    intent.putExtra(TvContractCompat.EXTRA_CHANNEL_ID, channelId);
    try {
       activity.startActivityForResult(intent, 0);
    } catch (ActivityNotFoundException e) {
      // handle error
    }
    

시스템에서 사용자에게 채널을 승인하도록 요청하는 대화상자를 표시합니다. 활동의 onActivityResult 메서드에서 요청 결과를 처리하세요(Activity.RESULT_CANCELED 또는 Activity.RESULT_OK).

Android TV 홈 화면 이벤트

사용자가 앱에서 게시한 프로그램/채널과 상호작용할 때 홈 화면에서 앱에 인텐트를 보냅니다.

  • 사용자가 채널의 로고를 선택하면 홈 화면에서 채널의 APP_LINK_INTENT_URI 속성에 저장된 Uri를 앱에 보냅니다. 앱에서는 기본 UI 또는 선택한 채널과 관련된 뷰를 시작하기만 하면 됩니다.
  • 사용자가 프로그램을 선택하면 홈 화면에서 프로그램의 INTENT_URI 속성에 저장된 Uri를 앱에 보냅니다. 앱에서는 선택한 콘텐츠를 재생하기만 하면 됩니다.
  • 사용자는 프로그램에 더 이상 관심이 없으며 이 프로그램을 홈 화면의 UI에서 삭제하고 싶다고 표시할 수 있습니다. 시스템이 프로그램을 UI에서 삭제하고 프로그램을 소유하는 앱에 인텐트(android.media.tv.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED 또는 android.media.tv.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED)를 프로그램의 ID와 함께 보냅니다. 앱이 프로그램을 제공자에서 삭제해야 하며 다시 삽입해서는 안 됩니다.

홈 화면에서 사용자 상호작용을 위해 보내는 모든 Uris에 관한 인텐트 필터를 만들어야 합니다. 예를 들어, 다음과 같습니다.

<receiver
       android:name=".WatchNextProgramRemoved"
       android:enabled="true"
       android:exported="true">
       <intent-filter>
           <action android:name="android.media.tv.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED" />
       </intent-filter>
    </receiver>
    

권장사항

  • 많은 TV 앱을 사용하려면 사용자가 로그인해야 합니다. 이 경우 android.media.tv.action.INITIALIZE_PROGRAMS를 수신 대기하는 BroadcastReceiver가 인증되지 않은 사용자를 위한 채널 콘텐츠를 추천해야 합니다. 예를 들어, 앱이 처음에 최고의 콘텐츠 또는 현재 인기 있는 콘텐츠를 표시할 수 있습니다. 사용자가 로그인한 후 맞춤설정된 콘텐츠를 표시할 수 있습니다. 이는 사용자가 로그인하기 전에 앱에서 사용자에게 업셀(up-sell)할 수 있는 좋은 기회입니다. leanback-homescreen-channels 샘플 앱은 앱이 설치된 후(또는 앱이 사전 설치된 경우 기기 설정 후) 채널을 로드하는 방법을 보여줍니다.
  • 앱이 포그라운드에 있지 않고 채널 또는 프로그램을 업데이트해야 하는 경우 JobScheduler를 사용하여 작업을 예약하세요(JobSchedulerJobService 참조).
  • 앱이 정상적으로 작동하지 않는 경우(예: 계속해서 제공자에게 데이터 스팸 발송) 시스템에서 앱 제공자 권한을 취소할 수 있습니다. 보안 예외를 처리하도록 try-catch 절을 사용하여 제공자에 액세스하는 코드를 래핑해야 합니다.
  • 프로그램 및 채널을 업데이트하기 전에 제공자에 업데이트해야 하는 데이터가 있는지 쿼리하고 데이터를 조정하세요. 예를 들어, 사용자가 UI에서 제거하려는 프로그램은 업데이트할 필요가 없습니다. 기존 데이터를 쿼리한 다음 채널의 승인을 요청한 후 데이터를 제공자에 삽입하거나 업데이트하는 백그라운드 작업을 사용하세요. 앱이 시작될 때와 앱이 데이터를 업데이트해야 할 때마다 이 작업을 실행할 수 있습니다.

    Kotlin

        context.contentResolver
          .query(
              TvContractCompat.buildChannelUri(channelId),
                  null, null, null, null).use({
                      cursor-> if (cursor != null and cursor.moveToNext()) {
                                   val channel = Channel.fromCursor(cursor)
                                   if (channel.isBrowsable()) {
                                       //update channel's programs
                                   }
                               }
                  })
        

    자바

        try (Cursor cursor = context.getContentResolver()
              .query(
                  TvContractCompat.buildChannelUri(channelId),
                  null,
                  null,
                  null,
                  null)) {
                      if (cursor != null && cursor.moveToNext()) {
                          Channel channel = Channel.fromCursor(cursor);
                          if (channel.isBrowsable()) {
                              //update channel's programs
                          }
                      }
                  }
        
  • 모든 이미지(로고, 아이콘, 콘텐츠 이미지)에 고유한 Uri를 사용하세요. 이미지를 업데이트하는 경우 다른 Uri를 사용해야 합니다. 모든 이미지는 캐시됩니다. 이미지를 변경할 때 Uri를 변경하지 않으면 이전 이미지가 계속 표시됩니다.

  • WHERE 절은 허용되지 않으며 WHERE 절을 사용하여 제공자를 호출하면 보안 예외가 발생합니다.

속성

이 섹션에서는 채널 및 프로그램 속성을 별도로 설명합니다.

채널 속성

모든 채널에 다음 속성을 지정해야 합니다.

속성 메모
유형 TYPE_PREVIEW로 설정합니다.
DISPLAY_NAME 채널의 이름으로 설정합니다.
APP_LINK_INTENT_URI 사용자가 채널의 로고를 선택하면 시스템에서 채널과 관련된 콘텐츠를 제공하는 활동을 시작하기 위해 인텐트를 보냅니다. 이 속성을 해당하는 활동의 인텐트 필터에서 사용된 Uri로 설정하세요.

또한 채널에는 내부 앱 사용을 위해 예약된 여섯 개의 필드가 있습니다. 이러한 필드는 앱이 채널을 내부 데이터 구조에 매핑할 때 도움이 될 수 있는 키 또는 다른 값을 저장하는 데 사용될 수 있습니다.

  • INTERNAL_PROVIDER_ID
  • INTERNAL_PROVIDER_DATA
  • INTERNAL_PROVIDER_FLAG1
  • INTERNAL_PROVIDER_FLAG2
  • INTERNAL_PROVIDER_FLAG3
  • INTERNAL_PROVIDER_FLAG4

프로그램 속성

각 프로그램 유형의 속성은 개별 페이지를 참조하세요.

샘플 코드

홈 화면과 상호작용하고 채널 및 프로그램을 Android TV 홈 화면에 추가하는 앱을 빌드하는 방법을 자세히 알아보려면 홈 화면 codelabgithub 샘플을 참조하세요.