Android 앱에 Cast 통합

이 개발자 가이드에서는 Android에 Google Cast 지원을 추가하는 방법을 설명합니다. 발신기 앱에 연결할 수 있습니다.

휴대기기 또는 노트북은 재생을 제어하는 발신자이며, Google Cast 기기는 TV에 콘텐츠를 표시하는 수신기입니다.

발신자 프레임워크는 Cast 클래스 라이브러리 바이너리 및 연결된 리소스 수에 따라 다릅니다 발신자 앱 또는 Cast 앱 발신자에서도 실행 중인 앱을 나타냅니다. 웹 수신기 앱 Cast 지원 기기에서 실행되는 HTML 애플리케이션을 의미합니다.

발신자 프레임워크는 비동기 콜백 설계를 사용하여 발신자에게 알립니다. 이벤트 앱 및 Cast 앱 수명 주기의 다양한 상태 간 전환 있습니다.

앱 흐름

다음 단계에서는 발신자의 일반적인 개략적인 실행 흐름을 설명합니다. Android 앱:

  • Cast 프레임워크는 자동으로 시작되며 MediaRouter Activity 수명 주기에 기반한 기기 검색
  • 사용자가 전송 버튼을 클릭하면 프레임워크에서 전송 버튼을 표시합니다. 검색된 Cast 기기 목록이 포함된 대화상자
  • 사용자가 Cast 기기를 선택하면 프레임워크는 Cast 기기의 웹 수신기 앱
  • 프레임워크는 발신기 앱에서 콜백을 호출하여 수신기 앱이 실행되었습니다.
  • 프레임워크는 발신자와 웹 사이에 통신 채널을 만듭니다. 수신기 앱
  • 프레임워크는 통신 채널을 사용하여 미디어를 로드하고 제어합니다. 웹 수신기에서 재생됩니다.
  • 프레임워크는 미디어 재생 상태를 보내는 발신자와 Web Receiver: 사용자가 발신자 UI 작업을 하면 프레임워크는 해당 미디어 제어 요청을 웹 수신기에 보내고, 웹 수신기가 미디어 상태 업데이트를 전송하면 프레임워크는 발신자 UI의 상태를 업데이트합니다.
  • 사용자가 전송 버튼을 클릭하여 Cast 기기의 연결을 해제할 때 프레임워크는 웹 수신기에서 발신기 앱의 연결을 해제합니다.

Google Cast의 모든 클래스, 메서드, 이벤트의 전체 목록은 Android SDK에 대한 자세한 내용은 Google Cast Sender API 참조 Android 다음 섹션에서는 Android 앱에 Cast를 추가하는 단계를 설명합니다.

Android 매니페스트 구성

앱의 AndroidManifest.xml 파일에서 다음을 구성해야 합니다. 요소를 사용하는 것이 좋습니다.

uses-sdk

Cast SDK에서 지원하는 최소 및 대상 Android API 수준을 설정합니다. 현재 최솟값은 API 수준 23이며 대상은 다음과 같습니다. API 수준 34

<uses-sdk
        android:minSdkVersion="23"
        android:targetSdkVersion="34" />

android:theme

최소 Android SDK 버전에 따라 앱의 테마를 설정합니다. 예를 들어 테마를 구현하는 것이 아니라면 최소 Android SDK 버전을 타겟팅할 때 Theme.AppCompat 출시합니다.

<application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat" >
       ...
</application>

전송 컨텍스트 초기화

프레임워크에는 전역 싱글톤 객체 CastContext가 있습니다. 이 객체는 모든 프레임워크 상호작용을 모니터링할 수 있습니다

앱은 다음을 구현해야 합니다. OptionsProvider 인터페이스를 통해 컨테이너 이미지를 초기화하는 데 필요한 CastContext 싱글톤입니다. OptionsProviderCastOptions 여기에는 프레임워크의 동작에 영향을 미치는 옵션이 포함되어 있습니다. 가장 이 중에서 중요한 웹 수신기 애플리케이션 ID는 검색 결과를 확인하고 Cast 세션이 있을 때 웹 수신기 앱을 실행할 수 있습니다. 시작됩니다

Kotlin
class CastOptionsProvider : OptionsProvider {
    override fun getCastOptions(context: Context): CastOptions {
        return Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .build()
    }

    override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
        return null
    }
}
자바
public class CastOptionsProvider implements OptionsProvider {
    @Override
    public CastOptions getCastOptions(Context context) {
        CastOptions castOptions = new CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .build();
        return castOptions;
    }
    @Override
    public List<SessionProvider> getAdditionalSessionProviders(Context context) {
        return null;
    }
}

구현된 OptionsProvider의 정규화된 이름을 선언해야 합니다. 메타데이터 필드로 설정합니다.

<application>
    ...
    <meta-data
        android:name=
            "com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
        android:value="com.foo.CastOptionsProvider" />
</application>

CastContextCastContext.getSharedInstance() 알 수 있습니다.

Kotlin
class MyActivity : FragmentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        val castContext = CastContext.getSharedInstance(this)
    }
}
자바
public class MyActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        CastContext castContext = CastContext.getSharedInstance(this);
    }
}

Cast UX 위젯

Cast 프레임워크는 Cast 디자인을 준수하는 위젯을 제공합니다. 체크리스트:

  • 소개 오버레이: 프레임워크는 맞춤 뷰를 제공하여 IntroductoryOverlay님, 사용자에게 표시되는 '전송' 버튼에 주의를 기울일 수 있습니다. 전달될 수 있습니다 Sender 앱에서 할 수 있는 작업 제목의 텍스트와 위치를 맞춤설정할 수 있습니다. 텍스트를 입력합니다.

  • 전송 버튼: 전송 버튼은 Cast 기기의 사용 가능 여부와 관계없이 표시됩니다. 사용자가 전송 버튼을 처음 클릭하면 전송 대화상자가 표시됩니다. 검색된 기기가 나열됩니다 사용자가 전송 버튼을 클릭할 때 기기가 연결되어 있는 동안에는 현재 미디어 메타데이터 (예: 제목, 녹음실 이름, 썸네일 이미지)을 제공하거나 사용자가 Cast 기기에서 연결을 해제하세요. '전송 버튼' 경우에 따라 '전송 아이콘'으로 설정할 수 있습니다.

  • 미니 컨트롤러: 사용자가 콘텐츠를 전송하고 현재 페이지에서 벗어났을 때 확장 컨트롤러가 발신기 앱의 다른 화면으로 전송되면 미니 컨트롤러가 화면 하단에 표시되어 사용자가 현재 전송 중인 미디어 메타데이터를 확인하고 재생을 제어합니다.

  • 확장 컨트롤러: 사용자가 콘텐츠를 전송할 때 미디어 알림을 클릭하는 경우 확장 컨트롤러가 실행되어 현재 재생 중인 미디어 메타데이터이며 제어할 수 있는 여러 버튼을 제공합니다 있습니다.

  • 알림: Android 전용. 사용자가 콘텐츠를 전송하면서 현재 전송 중인 기기를 보여주는 미디어 알림이 표시됨 미디어 메타데이터와 재생 컨트롤

  • 잠금 화면: Android 전용. 사용자가 콘텐츠를 전송하면서 탐색하거나 기기를 탐색할 때 시간이 초과되면 잠금 화면에 미디어 잠금 화면 컨트롤이 표시되어 현재 전송 중인 미디어 메타데이터와 재생 컨트롤이 표시됩니다.

다음 가이드에는 이러한 위젯을 다음에 추가하는 방법에 대한 설명이 포함되어 있습니다. 있습니다.

전송 버튼 추가

Android MediaRouter API는 보조 기기에서 미디어 표시 및 재생을 사용할 수 있도록 설계되었습니다. MediaRouter API를 사용하는 Android 앱에는 전송 버튼이 일부로 포함되어야 합니다. 를 사용하여 사용자가 미디어를 재생할 미디어 경로를 선택할 수 있도록 합니다. Cast 기기와 같은 보조 기기에서 작동합니다.

이 프레임워크는 MediaRouteButton . Cast button 아주 쉽습니다. 먼저 xml에 메뉴 항목 또는 MediaRouteButton를 추가해야 합니다. 파일을 만들고 CastButtonFactory 이를 프레임워크와 연결하는 것입니다.

// To add a Cast button, add the following snippet.
// menu.xml
<item
    android:id="@+id/media_route_menu_item"
    android:title="@string/media_route_menu_title"
    app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
    app:showAsAction="always" />
<ph type="x-smartling-placeholder">
</ph>
Kotlin
// Then override the onCreateOptionMenu() for each of your activities.
// MyActivity.kt
override fun onCreateOptionsMenu(menu: Menu): Boolean {
    super.onCreateOptionsMenu(menu)
    menuInflater.inflate(R.menu.main, menu)
    CastButtonFactory.setUpMediaRouteButton(
        applicationContext,
        menu,
        R.id.media_route_menu_item
    )
    return true
}
자바
// Then override the onCreateOptionMenu() for each of your activities.
// MyActivity.java
@Override public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    getMenuInflater().inflate(R.menu.main, menu);
    CastButtonFactory.setUpMediaRouteButton(getApplicationContext(),
                                            menu,
                                            R.id.media_route_menu_item);
    return true;
}

그런 다음 ActivityFragmentActivity님, 여기에 MediaRouteButton 추가할 수 있습니다.

// activity_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:gravity="center_vertical"
   android:orientation="horizontal" >

   <androidx.mediarouter.app.MediaRouteButton
       android:id="@+id/media_route_button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_weight="1"
       android:mediaRouteTypes="user"
       android:visibility="gone" />

</LinearLayout>
<ph type="x-smartling-placeholder">
</ph>
Kotlin
// MyActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_layout)

    mMediaRouteButton = findViewById<View>(R.id.media_route_button) as MediaRouteButton
    CastButtonFactory.setUpMediaRouteButton(applicationContext, mMediaRouteButton)

    mCastContext = CastContext.getSharedInstance(this)
}
자바
// MyActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_layout);

   mMediaRouteButton = (MediaRouteButton) findViewById(R.id.media_route_button);
   CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), mMediaRouteButton);

   mCastContext = CastContext.getSharedInstance(this);
}

테마를 사용하여 전송 버튼의 모양을 설정하려면 다음을 참고하세요. 전송 버튼 맞춤설정

기기 검색 구성

기기 검색은 Google Cloud의 CastContext CastContext 초기화 시 발신자 앱에서 웹 수신기를 지정 애플리케이션 ID로, 원하는 경우 설정에 따라 네임스페이스 필터링을 요청할 수 있습니다. supportedNamespaces인치 CastOptions CastContext는 내부적으로 MediaRouter 참조를 보유하며 검색 프로세스를 수행할 수 있습니다

  • 기기 검색 지연 시간과 기기 간 균형을 유지하도록 설계된 알고리즘 기반 경우에 따라서는 발신기 앱이 포그라운드로 전환될 때
  • 전송 대화상자가 열렸습니다.
  • Cast SDK에서 전송 세션 복구를 시도하고 있습니다.

전송 대화상자가 닫히거나 백그라운드로 전환됩니다.

Kotlin
class CastOptionsProvider : OptionsProvider {
    companion object {
        const val CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace"
    }

    override fun getCastOptions(appContext: Context): CastOptions {
        val supportedNamespaces: MutableList<String> = ArrayList()
        supportedNamespaces.add(CUSTOM_NAMESPACE)

        return CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .setSupportedNamespaces(supportedNamespaces)
            .build()
    }

    override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
        return null
    }
}
자바
class CastOptionsProvider implements OptionsProvider {
    public static final String CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace";

    @Override
    public CastOptions getCastOptions(Context appContext) {
        List<String> supportedNamespaces = new ArrayList<>();
        supportedNamespaces.add(CUSTOM_NAMESPACE);

        CastOptions castOptions = new CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .setSupportedNamespaces(supportedNamespaces)
            .build();
        return castOptions;
    }

    @Override
    public List<SessionProvider> getAdditionalSessionProviders(Context context) {
        return null;
    }
}

세션 관리 작동 방식

Cast SDK는 Cast 세션이라는 개념을 도입하여 기기 연결, 웹 실행 (또는 참여) 단계를 결합한 설정입니다. 수신기 앱, 해당 앱에 연결, 미디어 제어 채널 초기화. Web Receiver 보기 애플리케이션 수명 주기 가이드 를 참조하세요.

세션은 수업에서 관리합니다. SessionManager님, 앱에서 액세스할 수 있는 CastContext.getSessionManager() 개별 세션은 클래스의 서브클래스로 표현됨 Session 예를 들어 CastSession Cast 기기의 세션을 나타냅니다. 앱에서 현재 활성 상태인 다음을 통한 전송 세션: SessionManager.getCurrentCastSession()

앱은 SessionManagerListener 클래스를 사용하여 생성, 정지, 재개 및 있습니다. 프레임워크는 자동으로 재개를 시도하며 세션이 활성 상태일 때 비정상적/갑작스러운 종료

세션은 사용자 동작에 응답하여 자동으로 생성되고 해제됩니다. MediaRouter 대화상자에서 확인할 수 있습니다.

Cast 시작 오류를 더 잘 이해하기 위해 앱에서 CastContext#getCastReasonCodeForCastStatusCode(int) 세션 시작 오류를 CastReasonCodes 일부 세션 시작 오류 (예: CastReasonCodes#CAST_CANCELLED)가 발생했습니다. 의도된 동작이므로 오류로 기록되지 않아야 합니다.

세션의 상태 변경을 알아야 하는 경우 SessionManagerListener 이 예에서는 ActivityCastSession

Kotlin
class MyActivity : Activity() {
    private var mCastSession: CastSession? = null
    private lateinit var mCastContext: CastContext
    private lateinit var mSessionManager: SessionManager
    private val mSessionManagerListener: SessionManagerListener<CastSession> =
        SessionManagerListenerImpl()

    private inner class SessionManagerListenerImpl : SessionManagerListener<CastSession?> {
        override fun onSessionStarting(session: CastSession?) {}

        override fun onSessionStarted(session: CastSession?, sessionId: String) {
            invalidateOptionsMenu()
        }

        override fun onSessionStartFailed(session: CastSession?, error: Int) {
            val castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error)
            // Handle error
        }

        override fun onSessionSuspended(session: CastSession?, reason Int) {}

        override fun onSessionResuming(session: CastSession?, sessionId: String) {}

        override fun onSessionResumed(session: CastSession?, wasSuspended: Boolean) {
            invalidateOptionsMenu()
        }

        override fun onSessionResumeFailed(session: CastSession?, error: Int) {}

        override fun onSessionEnding(session: CastSession?) {}

        override fun onSessionEnded(session: CastSession?, error: Int) {
            finish()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mCastContext = CastContext.getSharedInstance(this)
        mSessionManager = mCastContext.sessionManager
        mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java)
    }

    override fun onResume() {
        super.onResume()
        mCastSession = mSessionManager.currentCastSession
    }

    override fun onDestroy() {
        super.onDestroy()
        mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession::class.java)
    }
}
자바
public class MyActivity extends Activity {
    private CastContext mCastContext;
    private CastSession mCastSession;
    private SessionManager mSessionManager;
    private SessionManagerListener<CastSession> mSessionManagerListener =
            new SessionManagerListenerImpl();

    private class SessionManagerListenerImpl implements SessionManagerListener<CastSession> {
        @Override
        public void onSessionStarting(CastSession session) {}
        @Override
        public void onSessionStarted(CastSession session, String sessionId) {
            invalidateOptionsMenu();
        }
        @Override
        public void onSessionStartFailed(CastSession session, int error) {
            int castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error);
            // Handle error
        }
        @Override
        public void onSessionSuspended(CastSession session, int reason) {}
        @Override
        public void onSessionResuming(CastSession session, String sessionId) {}
        @Override
        public void onSessionResumed(CastSession session, boolean wasSuspended) {
            invalidateOptionsMenu();
        }
        @Override
        public void onSessionResumeFailed(CastSession session, int error) {}
        @Override
        public void onSessionEnding(CastSession session) {}
        @Override
        public void onSessionEnded(CastSession session, int error) {
            finish();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCastContext = CastContext.getSharedInstance(this);
        mSessionManager = mCastContext.getSessionManager();
        mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession.class);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mCastSession = mSessionManager.getCurrentCastSession();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession.class);
    }
}

스트림 이전

스트림 전송의 기본은 세션 상태 보존입니다. 사용자는 음성 명령, Google Home을 사용하여 기존 오디오 및 동영상 스트림을 여러 기기로 옮길 수 있습니다. 앱 또는 스마트 디스플레이입니다. 미디어는 한 기기 (소스)에서 재생이 중지되고 다른 기기 (소스)에서 계속 재생됩니다. 대상)입니다. 최신 펌웨어가 설치된 모든 Cast 기기는 스트림 이전

스트림 이전 또는 확장 중에 새 대상 기기를 가져오려면 다음 단계를 따르세요. 등록 Cast.Listener 사용 CastSession#addCastListener 그런 다음 CastSession#getCastDevice() onDeviceNameChanged 콜백 사이에 위치합니다.

자세한 내용은 웹 수신기에서 스트림 전송 를 참조하세요.

자동 재연결

이 프레임워크는 ReconnectionService 발신기 앱이 이를 사용 설정하여 많은 섬세한 방법으로 재연결을 다음과 같은 특수한 경우를 예로 들 수 있습니다.

  • 일시적인 Wi-Fi 중단 복구
  • 기기 절전 모드 복구
  • 앱 백그라운드 상태에서 복구
  • 앱이 다운된 경우 복구

이 서비스는 기본적으로 사용 설정되어 있으며 사용 중지할 수 있습니다. CastOptions.Builder

자동 병합 시 이 서비스는 앱의 매니페스트에 자동으로 병합될 수 있습니다. 이 사용 설정되어 있는지 확인하세요.

프레임워크는 미디어 세션이 있을 때 서비스를 시작하고 중지합니다. 미디어 세션이 종료될 때

미디어 컨트롤 작동 방식

Cast 프레임워크는 RemoteMediaPlayer 클래스를 대체하는 새로운 클래스로 대체되었습니다. RemoteMediaClient님, 이는 보다 편리한 여러 API 집합에서 동일한 기능을 제공합니다. GoogleApiClient를 전달할 필요가 없습니다.

앱이 CastSession 미디어 네임스페이스를 지원하는 웹 수신기 앱을 사용하면 RemoteMediaClient는 프레임워크에서 자동으로 생성됩니다. 여러분의 앱은 CastSession에서 getRemoteMediaClient() 메서드를 호출하여 액세스합니다. 인스턴스를 만들 수 있습니다

웹 수신기에 요청을 실행하는 RemoteMediaClient의 모든 메서드는 해당 요청을 추적하는 데 사용할 수 있는 PendingResult 객체를 반환합니다.

RemoteMediaClient의 인스턴스를 다음에서 공유할 수 있습니다. 앱의 여러 부분, 그리고 실제로 앱의 영구 미니 컨트롤러알림 서비스도 제공합니다. 이를 위해 이 인스턴스는 RemoteMediaClient.Listener

미디어 메타데이터 설정

MediaMetadata 클래스는 전송하려는 미디어 항목에 관한 정보를 나타냅니다. 이 다음 예는 영화의 새 MediaMetadata 인스턴스를 만들고 두 개의 이미지

Kotlin
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)

movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle())
movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio())
movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(0))))
movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(1))))
자바
MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);

movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle());
movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio());
movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(0))));
movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(1))));

자세한 내용은 이미지 선택 미디어 메타데이터와 함께 이미지를 사용하는 방법에 대해 알아보겠습니다

미디어 로드

다음 코드와 같이 앱은 미디어 항목을 로드할 수 있습니다. 최초 사용 MediaInfo.Builder 미디어의 메타데이터와 함께 MediaInfo 인스턴스를 만들 수 있습니다 RemoteMediaClient 현재 CastSession에서 가져온 후 거기에 MediaInfo를 로드합니다. RemoteMediaClient입니다. RemoteMediaClient을(를) 사용하여 재생, 일시중지 등의 작업을 실행합니다. 웹 수신기에서 실행되는 미디어 플레이어 앱을 제어합니다.

Kotlin
val mediaInfo = MediaInfo.Builder(mSelectedMedia.getUrl())
    .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
    .setContentType("videos/mp4")
    .setMetadata(movieMetadata)
    .setStreamDuration(mSelectedMedia.getDuration() * 1000)
    .build()
val remoteMediaClient = mCastSession.getRemoteMediaClient()
remoteMediaClient.load(MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build())
자바
MediaInfo mediaInfo = new MediaInfo.Builder(mSelectedMedia.getUrl())
        .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
        .setContentType("videos/mp4")
        .setMetadata(movieMetadata)
        .setStreamDuration(mSelectedMedia.getDuration() * 1000)
        .build();
RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient();
remoteMediaClient.load(new MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build());

다음 섹션도 참조하세요. 미디어 트랙 사용

4K 동영상 형식

미디어의 동영상 형식을 확인하려면 다음을 사용하세요. getVideoInfo() 를 설정하여 현재 인스턴스를 VideoInfo 이 인스턴스에는 HDR TV 형식의 유형과 디스플레이 높이가 포함됩니다. 지정할 수 있습니다. 4K 형식의 변형은 상수로 표시됩니다. HDR_TYPE_*

여러 기기로 전송되는 리모컨 알림

사용자가 전송하면 같은 네트워크에 있는 다른 Android 기기에서도 재생을 제어할 수 있도록 알림을 만듭니다. 기기를 사용하는 모든 사용자 알림을 받으면 설정에서 해당 기기의 알림을 끌 수 있습니다. 앱 소개 > Google Cast > 리모컨 알림 표시 알림에는 설정 앱 바로가기가 포함됩니다. 자세한 내용은 리모컨 알림 전송

미니 컨트롤러 추가

전송 디자인에 따르면 체크리스트 발신기 앱은 무선 전송 장치라는 영구 제어 기능을 컨트롤러 사용자가 현재 콘텐츠 페이지에서 벗어나 전송할 수 있습니다. 미니 컨트롤러는 현재 전송 세션의 사용자에게 전송합니다. 미니 컨트롤러를 탭하면 Cast 전체 화면 확장형 컨트롤러 보기로 돌아갈 수 있습니다.

프레임워크는 맞춤 뷰인 MiniControllerFragment를 제공합니다. 이 뷰를 액티비티의 레이아웃 파일 맨 아래에 미니 컨트롤러입니다.

<fragment
    android:id="@+id/castMiniController"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:visibility="gone"
    class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment" />

발신기 앱이 동영상이나 오디오 라이브 스트림을 재생할 때 SDK는 재생/일시중지 버튼 대신 재생/중지 버튼이 자동으로 표시됩니다. 실행할 수 있습니다.

이 맞춤 뷰의 제목과 부제목의 텍스트 모양을 설정하려면 버튼을 선택하려면 미니 컨트롤러 맞춤설정

확장된 컨트롤러 추가

Google Cast 디자인 체크리스트에 따르면 발신기 앱은 확장된 컨트롤러 전송하려는 미디어입니다. 확장된 컨트롤러는 전체 화면 버전입니다. 만드는 것입니다.

Cast SDK는 ExpandedControllerActivity 이는 추상 클래스로 Cast 버튼을 추가하기 위해 서브클래스로 선언해야 합니다.

먼저 확장된 컨트롤러가 제공할 새 메뉴 리소스 파일을 만듭니다. 전송 버튼:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
            android:id="@+id/media_route_menu_item"
            android:title="@string/media_route_menu_title"
            app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
            app:showAsAction="always"/>

</menu>

ExpandedControllerActivity를 확장하는 새 클래스를 만듭니다.

Kotlin
class ExpandedControlsActivity : ExpandedControllerActivity() {
    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        super.onCreateOptionsMenu(menu)
        menuInflater.inflate(R.menu.expanded_controller, menu)
        CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item)
        return true
    }
}
자바
public class ExpandedControlsActivity extends ExpandedControllerActivity {
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        getMenuInflater().inflate(R.menu.expanded_controller, menu);
        CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item);
        return true;
    }
}

이제 application 태그 내의 앱 매니페스트에서 새 활동을 선언합니다.

<application>
...
<activity
        android:name=".expandedcontrols.ExpandedControlsActivity"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:theme="@style/Theme.CastVideosDark"
        android:screenOrientation="portrait"
        android:parentActivityName="com.google.sample.cast.refplayer.VideoBrowserActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
    </intent-filter>
</activity>
...
</application>

CastOptionsProvider를 수정하고 NotificationOptions를 다음과 같이 변경합니다. CastMediaOptions: 타겟 활동을 새 활동으로 설정합니다.

Kotlin
override fun getCastOptions(context: Context): CastOptions? {
    val notificationOptions = NotificationOptions.Builder()
        .setTargetActivityClassName(ExpandedControlsActivity::class.java.name)
        .build()
    val mediaOptions = CastMediaOptions.Builder()
        .setNotificationOptions(notificationOptions)
        .setExpandedControllerActivityClassName(ExpandedControlsActivity::class.java.name)
        .build()

    return CastOptions.Builder()
        .setReceiverApplicationId(context.getString(R.string.app_id))
        .setCastMediaOptions(mediaOptions)
        .build()
}
자바
public CastOptions getCastOptions(Context context) {
    NotificationOptions notificationOptions = new NotificationOptions.Builder()
            .setTargetActivityClassName(ExpandedControlsActivity.class.getName())
            .build();
    CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
            .setNotificationOptions(notificationOptions)
            .setExpandedControllerActivityClassName(ExpandedControlsActivity.class.getName())
            .build();

    return new CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .setCastMediaOptions(mediaOptions)
            .build();
}

LocalPlayerActivity loadRemoteMedia 메서드를 업데이트하여 새 Activity를 호출합니다.

Kotlin
private fun loadRemoteMedia(position: Int, autoPlay: Boolean) {
    val remoteMediaClient = mCastSession?.remoteMediaClient ?: return

    remoteMediaClient.registerCallback(object : RemoteMediaClient.Callback() {
        override fun onStatusUpdated() {
            val intent = Intent(this@LocalPlayerActivity, ExpandedControlsActivity::class.java)
            startActivity(intent)
            remoteMediaClient.unregisterCallback(this)
        }
    })

    remoteMediaClient.load(
        MediaLoadRequestData.Builder()
            .setMediaInfo(mSelectedMedia)
            .setAutoplay(autoPlay)
            .setCurrentTime(position.toLong()).build()
    )
}
자바
private void loadRemoteMedia(int position, boolean autoPlay) {
    if (mCastSession == null) {
        return;
    }
    final RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient();
    if (remoteMediaClient == null) {
        return;
    }
    remoteMediaClient.registerCallback(new RemoteMediaClient.Callback() {
        @Override
        public void onStatusUpdated() {
            Intent intent = new Intent(LocalPlayerActivity.this, ExpandedControlsActivity.class);
            startActivity(intent);
            remoteMediaClient.unregisterCallback(this);
        }
    });
    remoteMediaClient.load(new MediaLoadRequestData.Builder()
            .setMediaInfo(mSelectedMedia)
            .setAutoplay(autoPlay)
            .setCurrentTime(position).build());
}

발신기 앱이 동영상이나 오디오 라이브 스트림을 재생할 때 SDK는 재생/일시중지 버튼 대신 재생/중지 버튼이 자동으로 표시됩니다. 사용할 수 있습니다.

테마를 사용하여 모양을 설정하려면 표시할 버튼을 선택하고 맞춤검색 버튼을 추가하려면 확장 컨트롤러 맞춤설정

볼륨 제어

프레임워크는 발신기 앱의 볼륨을 자동으로 관리합니다. 프레임워크 발신자와 웹 수신기 앱을 자동으로 동기화하여 UI는 항상 웹 수신기에서 지정한 볼륨을 보고합니다.

물리적 버튼 볼륨 컨트롤

Android에서는 발신기 기기의 물리적 버튼을 사용하여 기본적으로 웹 수신기에 있는 Cast 세션 볼륨 조절 장치를 Jelly Bean 이상

Jelly Bean 이전의 물리적 버튼 볼륨 제어

실제 볼륨 키를 사용하여 웹 수신기 기기의 볼륨을 조절하는 방법 Jelly Bean보다 오래된 Android 기기의 경우 발신기 앱이 재정의해야 합니다. dispatchKeyEvent 를 사용하고, CastContext.onDispatchVolumeKeyEventBeforeJellyBean():

Kotlin
class MyActivity : FragmentActivity() {
    override fun dispatchKeyEvent(event: KeyEvent): Boolean {
        return (CastContext.getSharedInstance(this)
            .onDispatchVolumeKeyEventBeforeJellyBean(event)
                || super.dispatchKeyEvent(event))
    }
}
자바
class MyActivity extends FragmentActivity {
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        return CastContext.getSharedInstance(this)
            .onDispatchVolumeKeyEventBeforeJellyBean(event)
            || super.dispatchKeyEvent(event);
    }
}

알림 및 잠금 화면에 미디어 컨트롤 추가

Google Cast 디자인 체크리스트에 따라 발신기 앱은 Android에서만 하나의 캠페인에서 미디어 컨트롤을 알림 자물쇠에 화면, 발신자가 전송 중이지만 발신 앱에 포커스가 없는 경우입니다. 이 프레임워크가 제공하는 MediaNotificationServiceMediaIntentReceiver 발신기 앱이 알림과 잠금에 미디어 컨트롤을 빌드할 수 있도록 지원합니다. 화면

MediaNotificationService는 발신자가 전송 중일 때 실행되며 현재 전송에 대한 정보와 이미지 썸네일이 포함된 알림 재생/일시중지 버튼, 중지 버튼이 있습니다.

MediaIntentReceiver는 다음과 같은 사용자 작업을 처리하는 BroadcastReceiver입니다. 있습니다.

앱이 다음을 통해 잠금 화면에서 알림 및 미디어 제어를 구성할 수 있습니다. NotificationOptions 앱은 알림에 표시할 컨트롤 버튼을 구성할 수 있습니다. 사용자가 알림을 탭하면 열리는 Activity입니다. If 작업 명시적으로 제공되지 않으면 기본값 MediaIntentReceiver.ACTION_TOGGLE_PLAYBACKMediaIntentReceiver.ACTION_STOP_CASTING가 사용됩니다.

Kotlin
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting".
val buttonActions: MutableList<String> = ArrayList()
buttonActions.add(MediaIntentReceiver.ACTION_REWIND)
buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK)
buttonActions.add(MediaIntentReceiver.ACTION_FORWARD)
buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING)

// Showing "play/pause" and "stop casting" in the compat view of the notification.
val compatButtonActionsIndices = intArrayOf(1, 3)

// Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds.
// Tapping on the notification opens an Activity with class VideoBrowserActivity.
val notificationOptions = NotificationOptions.Builder()
    .setActions(buttonActions, compatButtonActionsIndices)
    .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS)
    .setTargetActivityClassName(VideoBrowserActivity::class.java.name)
    .build()
자바
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting".
List<String> buttonActions = new ArrayList<>();
buttonActions.add(MediaIntentReceiver.ACTION_REWIND);
buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK);
buttonActions.add(MediaIntentReceiver.ACTION_FORWARD);
buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING);

// Showing "play/pause" and "stop casting" in the compat view of the notification.
int[] compatButtonActionsIndices = new int[]{1, 3};

// Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds.
// Tapping on the notification opens an Activity with class VideoBrowserActivity.
NotificationOptions notificationOptions = new NotificationOptions.Builder()
    .setActions(buttonActions, compatButtonActionsIndices)
    .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS)
    .setTargetActivityClassName(VideoBrowserActivity.class.getName())
    .build();

다음에서 알림 및 잠금 화면의 미디어 컨트롤 표시를 사용 설정함 기본적으로 설정되며, setNotificationOptions 다음에 null 포함 CastMediaOptions.Builder 현재 알림이 사용 설정되어 있으면 잠금 화면 기능이 사용 설정되어 있습니다. 사용 설정되어 있는지 확인합니다.

Kotlin
// ... continue with the NotificationOptions built above
val mediaOptions = CastMediaOptions.Builder()
    .setNotificationOptions(notificationOptions)
    .build()
val castOptions: CastOptions = Builder()
    .setReceiverApplicationId(context.getString(R.string.app_id))
    .setCastMediaOptions(mediaOptions)
    .build()
자바
// ... continue with the NotificationOptions built above
CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
        .setNotificationOptions(notificationOptions)
        .build();
CastOptions castOptions = new CastOptions.Builder()
        .setReceiverApplicationId(context.getString(R.string.app_id))
        .setCastMediaOptions(mediaOptions)
        .build();

발신기 앱이 동영상이나 오디오 라이브 스트림을 재생할 때 SDK는 재생/일시중지 버튼 대신 재생/중지 버튼이 자동으로 표시됩니다. 알림 컨트롤에 있어야 하지만 잠금 화면 컨트롤에는 적용되지 않습니다.

참고: Lollipop 이전 기기에서 잠금 화면 컨트롤을 표시하려면 RemoteMediaClient에서 사용자를 대신하여 자동으로 오디오 포커스를 요청합니다.

오류 처리

발신기 앱이 모든 오류 콜백을 처리하고 최적의 응답을 찾습니다. 앱은 표시할 수 있는 해당 애플리케이션에 대한 연결을 해제하거나 웹 수신기