Статьи

Сервисы Google Play: Google Cast v3 и Media

Google Cast — это технология, которая позволяет пользователям отправлять онлайн-контент на устройства, такие как Chromecast или Android TV, подключенные к телевизору. Как только контент будет доступен на телевидении, пользователи смогут управлять им со своего мобильного устройства или компьютера.

В этом руководстве вы узнаете, как создать базовое приложение с поддержкой Cast для Android с использованием Cast SDK v3, анонсированного во время конференции Google I / O 2016 года.

В состав Google Cast входят два компонента: получатель, который, по сути, представляет собой веб-страницу, отображаемую на устройстве кастинга с вашим контентом, и отправитель, который является клиентской программой, которая запрашивает мультимедиа и управляет воспроизведением.

Прежде чем вы сможете создать приложение отправителя, вам необходимо зарегистрировать учетную запись в консоли разработчика Google Cast , а затем создать и настроить новое приложение получателя. Чтобы зарегистрировать аккаунт, вам нужно будет заплатить единовременный сбор в размере 5 долларов США. После создания учетной записи вы можете нажать на красную кнопку «Добавить новое приложение», чтобы создать новое приложение получателя.

Далее у вас будет три варианта: Пользовательский приемник, Стилизованный медиаресивер и Удаленный дисплейный приемник. Для простоты в этом руководстве вы будете использовать Styled Media Receiver.

Новые типы приложений приемника

На следующем экране вы сможете выбрать некоторые основные настройки для вашего приемника, такие как имя приложения, необязательный URL-адрес для таблицы стилей CSS, чтобы настроить внешний вид приемника , а также возможность включить гостевой режим и аудио. только кастинг.

Параметры настройки для нового стиля медиа-ресивера

После того, как вы нажмете синюю кнопку Сохранить , вы увидите экран, который покажет вам основные детали вашего нового приложения приемника. Вы заметите, что этот экран также содержит ваш новый идентификатор приложения . Вам нужно будет использовать это значение в вашем приложении Android.

Экран, показывающий информацию для вновь созданного получателя

Стоит отметить, что, несмотря на то, что приложение-получатель создано, обнаружение приложения-отправителя может занять несколько часов.

Чтобы протестировать, вам нужно внести в белый список хотя бы одно устройство для литья. Это можно сделать из консоли разработчика Google Cast, нажав красную кнопку ДОБАВИТЬ НОВОЕ УСТРОЙСТВО . На появившемся экране вы можете ввести серийный номер вашего устройства и описание, чтобы внести его в белый список для тестирования с приложением вашего приемника.

Диалог для белого списка устройств физического литья

На этом этапе у вас должен быть создан приемник и тестовое устройство, занесенное в белый список, так что все готово для создания приложения отправителя Android. Создав и опубликовав свое приложение в Play Store, вы захотите вернуться в Cast Developer Console, чтобы опубликовать свой получатель, позволяя использовать любое устройство приведения с вашим приложением-отправителем.

Первое, что вам нужно сделать в своем приложении для Android, — это включить библиотеки Cast Framework и Media Router под узлом dependencies в файле build.gradle .

1
2
compile ‘com.android.support:mediarouter-v7:24.1.1’
compile ‘com.google.android.gms:play-services-cast-framework:9.4.0’

Затем вы захотите сохранить идентификатор приложения, который вы получили при создании получателя, в файле strings.xml .

1
<string name=»cast_app_id»>(your ID goes here)</string>

Последний шаг в процессе установки включает в себя интернет-разрешение для вашего приложения. Откройте AndroidManifest.xml и добавьте следующую строку перед узлом application .

1
<uses-permission android:name=»android.permission.INTERNET» />

Теперь, когда настройка завершена, вы можете перейти к включению кнопки медиа-маршрута в ваше приложение.

Кнопка маршрутизации — это значок на панели инструментов приложения, который обычно означает, что приложение поддерживает приведение для пользователя.

Кнопка медиа-роутера

Чтобы эта кнопка появилась на Toolbar вашего приложения, проще всего включить ее в XML-файл меню для вашего Activity (также рекомендуется, чтобы это было включено в каждое действие в вашем приложении).

1
2
3
4
5
6
7
8
9
<?xml version=»1.0″ encoding=»utf-8″?>
<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=»Cast»
        app:actionProviderClass=»android.support.v7.app.MediaRouteActionProvider»
        app:showAsAction=»always» />
</menu>

Затем вам нужно будет инициализировать этот новый MenuItem в методе onCreateOptionsMenu вашей Activity .

1
2
3
4
5
6
7
8
9
@Override public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    getMenuInflater().inflate(R.menu.menu_main, menu);
    mMediaRouterButton = CastButtonFactory.setUpMediaRouteButton(getApplicationContext(),
            menu,
            R.id.media_route_menu_item);
 
    return true;
}

После инициализации кнопки медиа-маршрута вы захотите добавить слушатели состояния в ваше приложение для приведения.

В то время как доступно несколько прослушивателей, есть три, которые стоит обсудить, когда вы начнете использовать каркас Google Cast.

  • CastStateListener : этот слушатель контролирует текущее состояние приведения приложения. Это срабатывает, когда приложение переключилось на CONNECTING , NOT_CONNECTED , NO_DEVICES_AVAILABLE или NO_DEVICES_AVAILABLE .
  • AppVisibilityListener : у этого слушателя есть два метода: onAppEnteredForeground и onAppEnteredBackground . Эти методы вызываются, когда ваше приложение было фоновым для вашего пользователя, или когда пользователь снова открыл ваше приложение, соответственно.
  • SessionManagerListener : последний слушатель, которого мы пройдем, также самый многословный. Session — это жизненный цикл взаимодействия пользователя с устройством преобразования, начинающийся, когда пользователь подключился к устройству, поддерживаемый посредством преобразования и заканчивающийся, когда пользователь отключен. Платформа Google Cast Android взаимодействует с Session через объект SessionManager .

Эти три слушателя могут быть связаны с платформой Google Cast следующим образом, где в данном примере это Activity , в которой реализован каждый из вышеуказанных интерфейсов.

1
2
3
CastContext.getSharedInstance(this).addCastStateListener(this);
CastContext.getSharedInstance(this).addAppVisibilityListener(this);
CastContext.getSharedInstance(this).getSessionManager().addSessionManagerListener(this);

Возможно, вы также заметили, что вы SessionManager доступ к SessionManager и платформе Cast, используя CastContext.getSharedInstance(Context) . Это потому, что CastContext , основная точка взаимодействия между вашим приложением и платформой Cast, лениво инициализируется для повышения производительности приложения.

Когда ваша Activity больше не активна, вам нужно помнить, чтобы удалить этих слушателей.

1
2
3
CastContext.getSharedInstance(this).removeAppVisibilityListener(this);
CastContext.getSharedInstance(this).removeCastStateListener(this);
CastContext.getSharedInstance(this).getSessionManager().removeSessionManagerListener(this);

Чтобы сделать что-либо с платформой Cast, вам нужно создать новый класс, который расширяет OptionsProvider . В этом классе вы сможете настроить различные параметры для своего приложения отправителя.

Мы пока оставим это простым и просто CastOptions объект CastOptions из метода getCastOptions , который позволит возобновить сохраненные сеансы и повторно подключиться к сеансам, которые уже выполняются (хотя оба они уже включены по умолчанию, они представлены здесь как Примеры).

Объект CastOptions также является тем, где идентификатор приложения получателя связан с вашим отправителем. Хотя метод getAdditionalSessionProviders должен быть объявлен в этом классе, мы можем смело игнорировать его для наших целей.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public class CastOptionsProvider implements OptionsProvider {
    @Override
    public CastOptions getCastOptions(Context context) {
 
        CastOptions castOptions = new CastOptions.Builder()
                .setResumeSavedSession(true)
                .setEnableReconnectionService(true)
                .setReceiverApplicationId(context.getString(R.string.cast_app_id))
                .build();
 
        return castOptions;
    }
 
    @Override
    public List<SessionProvider> getAdditionalSessionProviders(Context context) {
        return null;
    }
}

Вам также нужно будет включить этот класс в файл AndroidManifest.xml в тег meta-data под узлом application .

1
2
3
<meta-data
   android:name=»com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME»
   android:value=»com.tutsplus.googlecastv3.CastOptionsProvider» />

На этом этапе ваше приложение должно быть в состоянии найти любые литейные устройства из белого списка и подключиться к ним через ваше приложение.

Диалог маршрутизации при подключении к устройству для литья, но не к

В зависимости от темы, которую вы используете в своем приложении (например, Theme.AppCompat.Light.NoActionBar ), вы могли заметить некоторые странные поведения с цветами в диалоговом окне устройства приведения, такие как белый шрифт и значки на белом фоне.

Проблема стилей диалога маршрутизации

Вы также можете решить, что хотите настроить внешний вид диалога в соответствии с вашим приложением. Вы можете сделать это, переопределив два стиля, используемых для диалога Cast: Theme.MediaRouter.Light.DarkControlPanel и Theme.MediaRouter.LightControlPanel . Например, если вы используете белый шрифт на белом фоне, вы можете включить следующий код в свой файл styles.xml, чтобы изменить значки и цвет шрифта на черный на белом фоне.

01
02
03
04
05
06
07
08
09
10
11
12
13
<style name=»Theme.MediaRouter.Light.DarkControlPanel»>
    <item name=»mediaRoutePlayDrawable»>@drawable/mr_ic_play_light</item>
    <item name=»mediaRoutePauseDrawable»>@drawable/mr_ic_pause_light</item>
    <item name=»mediaRouteCastDrawable»>@drawable/mr_ic_cast_dark</item>
    <item name=»mediaRouteAudioTrackDrawable»>@drawable/ic_audiotrack_light</item>
    <item name=»mediaRouteControllerPrimaryTextStyle»>@style/MediaRouteControllerTextColor</item>
    <item name=»mediaRouteControllerSecondaryTextStyle»>@style/MediaRouteControllerTextColor</item>
</style>
 
<style name=»MediaRouteControllerTextColor»>
    <item name=»android:textColor»>#000000</item>
    <item name=»android:textSize»>14sp</item>
</style>

После подключения к устройству кастинга вы, вероятно, захотите разрешить своим пользователям транслировать контент на него. К счастью, Cast SDK делает это невероятно простым. В вашем приложении вы захотите определить, подключен ли ваш пользователь к устройству, что можно сделать, убедившись, что SessionManager имеет текущий Session и что текущий Session имеет связанный с ним объект RemoteMediaClient .

1
2
if( CastContext.getSharedInstance(this).getSessionManager().getCurrentCastSession() != null
      && CastContext.getSharedInstance(this).getSessionManager().getCurrentCastSession().getRemoteMediaClient() != null ) {

Как только вы узнаете, что приложение связано с RemoteMediaClient , вы захотите создать объект MediaInfo , содержащий ссылку на удаленный контент, который вы хотите воспроизвести, а также типы потоков и контента для вашего мультимедиа. Когда MediaInfo создан и заполнен, вы можете вызвать метод load в RemoteMediaClient чтобы начать приведение содержимого. Например, следующий код переведет видео файл на телевизор.

1
2
3
4
5
6
7
8
9
RemoteMediaClient remoteMediaClient = CastContext.getSharedInstance(this).getSessionManager().getCurrentCastSession().getRemoteMediaClient();
 
MediaInfo mediaInfo = new MediaInfo.Builder(getString(R.string.movie_link))
        .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
        .setContentType(«videos/mp4»)
        .build();
 
 
remoteMediaClient.load(mediaInfo, true, 0);
Диалог маршрутизации при подключении к устройству

Компоненты приемника и пользовательского интерфейса в Cast SDK используют объект MediaMetadata для хранения и ссылки на информацию о медиафайлах, которые воспроизводятся в данный момент. Вы можете добавлять значения к этому объекту, используя ключи, предоставленные классом, и вы можете добавлять URL-адреса изображений, используя метод addImage .

1
2
3
4
5
MediaMetadata metadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
 
metadata.putString(MediaMetadata.KEY_TITLE, «Title»);
metadata.putString(MediaMetadata.KEY_SUBTITLE, «Subtitle»);
metadata.addImage(new WebImage(Uri.parse(getString(R.string.movie_poster))));

Как MediaMetadata объект MediaMetadata создан, вы можете связать его с MediaInfo содержимого.

1
2
3
4
5
MediaInfo mediaInfo = new MediaInfo.Builder(getString(R.string.movie_link))
       .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
       .setContentType(«videos/mp4»)
       .setMetadata(metadata)
       .build();

Хотя Cast SDK обрабатывает логику подключения и трансляции контента на телевизор, он также предоставляет несколько компонентов пользовательского интерфейса, которые помогают разработчикам соответствовать рекомендациям по проектированию Casting UI .

Когда ваш пользователь впервые открывает ваше приложение, рекомендуется сообщить ему, что вы поддерживаете Google Cast. Вы можете сделать это, включив IntroductoryOverlay , который подсвечивает кнопку приведения, когда она становится доступной в первый раз.

Чтобы включить IntroductoryOverlay , первое, что вам нужно сделать, это добавить его в качестве переменной-члена в верхней части вашего основного действия.

1
private IntroductoryOverlay mIntroductoryOverlay;

Если у вас есть общий объект для наложения, вы можете создать метод, который будет проверять, отображается ли кнопка медиа-маршрутизатора, и, если он отображается, отображает наложение.

Этот компонент реализован с использованием простого шаблона компоновщика, который принимает String для текста, идентификатор ресурса цвета и несколько других атрибутов настройки. Чаще всего вы также захотите убедиться, что вы вызываете setSingleTime() , чтобы наложение отображалось только один раз для пользователя.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private void showIntroductoryOverlay() {
    if (mIntroductoryOverlay != null) {
        mIntroductoryOverlay.remove();
    }
    if ((mMediaRouterButton != null) && mMediaRouterButton.isVisible()) {
        new Handler().post(new Runnable() {
            @Override
            public void run() {
                mIntroductoryOverlay = new IntroductoryOverlay.Builder(
                        MainActivity.this, mMediaRouterButton)
                        .setTitleText(«Introduction text»)
                        .setOverlayColor(R.color.colorPrimary)
                        .setSingleTime()
                        .setOnOverlayDismissedListener(
                                new IntroductoryOverlay.OnOverlayDismissedListener() {
                                    @Override
                                    public void onOverlayDismissed() {
                                        mIntroductoryOverlay = null;
                                    }
                                })
                        .build();
                mIntroductoryOverlay.show();
            }
        });
    }
}

Теперь, когда у вас есть метод, созданный для отображения наложения, вам просто нужно будет вызвать его. Есть два момента, где вы должны добавить этот метод: в onCreateOptionsMenu и в onCastStateChanged из вашего CastStateListener когда состояние не NO_DEVICES_AVAILABLE . Это позволит справиться с обоими непредвиденными обстоятельствами появления кнопки маршрутизации.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
@Override
public void onCastStateChanged(int newState) {
    if (newState != CastState.NO_DEVICES_AVAILABLE) {
        showIntroductoryOverlay();
    }
}
 
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    getMenuInflater().inflate(R.menu.menu_main, menu);
    mMediaRouterButton = CastButtonFactory.setUpMediaRouteButton(getApplicationContext(),
            menu,
            R.id.media_route_menu_item);
 
    showIntroductoryOverlay();
 
    return true;
}

На этом этапе вы сможете запустить свое приложение и увидеть наложение, как показано на следующем рисунке. Если вам нужно увидеть его снова в целях тестирования, вы можете очистить данные своего приложения и снова открыть.

Вводный пример наложения

Во время трансляции вы захотите предоставить простой виджет пользовательского интерфейса для управления контентом на телевизоре пользователя. Google упростил это, предоставив класс ExpandedControllerActivity в Cast SDK.

Чтобы использовать это, создайте новый класс Java и расширьте ExpandedControllerActivity . Этот учебник создаст один с именем ExpandedControlsActivity . Как только ваша деятельность будет создана, обновите onCreateOptionsMenu чтобы включить кнопку маршрутизации приведения на панель инструментов.

01
02
03
04
05
06
07
08
09
10
public class ExpandedControlsActivity extends ExpandedControllerActivity {
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        getMenuInflater().inflate(R.menu.menu_main, menu);
        CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item);
        return true;
    }
}

Затем откройте ваш класс OptionsProvider . Вы захотите перейти в метод getCastOptions и создать объект CastMediaOptions который связан с вашим ExpandedControllerActivity . Как только ваш объект CastMediaOptions создан, вы можете связать его с элементом CastOptions который возвращается методом.

01
02
03
04
05
06
07
08
09
10
11
12
CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
        .setExpandedControllerActivityClassName(ExpandedControlsActivity.class.getName())
        .build();
 
CastOptions castOptions = new CastOptions.Builder()
        .setResumeSavedSession(true)
        .setEnableReconnectionService(true)
        .setReceiverApplicationId(context.getString(R.string.cast_app_id))
        .setCastMediaOptions(mediaOptions)
        .build();
 
return castOptions;

Наконец, чтобы получить работающий ExpandedControllerActivity , вам нужно будет включить его в AndroidManifest.xml , вот так.

01
02
03
04
05
06
07
08
09
10
11
12
13
<activity
    android:name=».ExpandedControlsActivity»
    android:label=»@string/app_name»
    android:theme=»@style/ExpandedCastControlsStyle»
    android:launchMode=»singleTask»
    android:screenOrientation=»portrait»>
    <intent-filter>
        <action android:name=»android.intent.action.MAIN»/>
    </intent-filter>
    <meta-data
        android:name=»android.support.PARENT_ACTIVITY»
        android:value=».MainActivity»/>
</activity>

Вы должны заметить, что для узла activity установлен набор свойств theme . Этот стиль используется для дополнительной настройки ExpandedControllerActivity и отображаемых кнопок.

Контроллер состоит из четырех настраиваемых слотов кнопок, с переключателем воспроизведения / паузы в середине. Используя новый стиль и ресурсы массива, вы можете настроить, какие кнопки будут отображаться. В arrays.xml я добавил новый array котором слот 1 становится пустым, слот 2 — 30-секундной кнопкой перемотки назад, слот 3 (первый элемент справа от переключателя воспроизведения / паузы) — для быстрой перемотки вперед на 30 секунд и последний слот для размещения отключения звука.

1
2
3
4
5
6
7
8
9
<?xml version=»1.0″ encoding=»utf-8″?>
<resources>
    <array name=»cast_expanded_controller_control_buttons»>
        <item>@id/cast_button_type_empty</item>
        <item>@id/cast_button_type_rewind_30_seconds</item>
        <item>@id/cast_button_type_forward_30_seconds</item>
        <item>@id/cast_button_type_mute_toggle</item>
    </array>
</resources>

Затем вы можете связать этот array с вашим Activity , создав новый ресурс style и переопределив значение castExpandedControllerStyle новым style который расширяет CastExpandedController .

1
2
3
4
5
6
7
<style name=»ExpandedCastControlsStyle» parent=»Theme.AppCompat.NoActionBar»>
    <item name=»castExpandedControllerStyle»>@style/CustomCastExpandedController</item>
</style>
 
<style name=»CustomCastExpandedController» parent=»CastExpandedController»>
    <item name=»castControlButtons»>@array/cast_expanded_controller_control_buttons</item>
</style>

На этом этапе вы должны иметь возможность щелкнуть изображение в диалоговом окне маршрутизатора, чтобы вставить носитель, чтобы открыть новый контроллер Activity , или запустить его из приложения самостоятельно с помощью простого вызова startActivity .

1
startActivity(new Intent(this, ExpandedControlsActivity.class));
Пример ExpandedControllerActivity

Когда пользователь загружает контент на свой телевизор, есть большая вероятность, что он не будет держать ваше приложение на переднем плане или его телефон разблокирован. Когда они уходят от вашего приложения, вы захотите предоставить им простой способ управления содержимым вашего приложения. Вы можете сделать это, добавив уведомление в свое приложение, когда оно не находится на переднем плане для устройств Lollipop и выше, и Cast SDK будет обрабатывать создание экрана блокировки RemoteControlClient для KitKat и более ранних устройств.

getCastOptions элементы управления уведомлением / блокировкой экрана довольно просто, поскольку все это обрабатывается в методе getCastOptions вашего OptionsProvider ( CastOptionsProvider.java для этого урока).

Во-первых, вам нужно будет создать ArrayList строк, который будет содержать кнопки, которые вы хотите использовать для своих элементов управления. Затем вы можете создать массив int который будет содержать индексы кнопок, которые вы хотите отобразить, когда уведомление находится в компактном режиме.

Создав два массива, вы создадите объект NotificationOptions который привязывает действия к новому уведомлению и назначит действие, которое будет открыто при выборе уведомления. Для этого примера мы просто будем использовать ExpandedControlsActivity который мы создали в последнем разделе.

Наконец, вы можете добавить уведомление в ваши CastMediaOptions .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
List<String> buttonActions = new ArrayList<>();
 
buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK);
buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING);
 
int[] compatButtonActionsIndicies = new int[]{ 0, 1 };
 
NotificationOptions notificationOptions = new NotificationOptions.Builder()
        .setActions(buttonActions, compatButtonActionsIndicies)
        .setTargetActivityClassName(ExpandedControlsActivity.class.getName())
        .build();
 
CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
        .setNotificationOptions(notificationOptions)
        .setExpandedControllerActivityClassName(ExpandedControlsActivity.class.getName())
        .build();

Теперь, когда ваши пользователи загружают контент на свои телевизоры и блокируют экран или уходят от вашего приложения, появляется уведомление, позволяющее им контролировать контент на большом экране, продолжая взаимодействовать со своим телефоном. Нажатие на уведомление вне элементов управления вернет ваше приложение на передний план с ExpandedControlsActivity , предоставляя вашим пользователям более детальный контроль над их впечатлениями от просмотра.

Уведомление на примере экрана блокировки в Lollipop

Последний виджет пользовательского интерфейса, о котором вы узнаете в этом руководстве, — это MiniControllerFragment . Этот элемент может быть помещен в файлы макета ваших действий, и когда ваше приложение будет транслировать контент, он автоматически станет видимым и предоставит легко доступный контроллер для ваших пользователей при просмотре вашего приложения. Хотя это последний компонент, который мы обсудим, он также является самым простым в реализации. Вам просто нужно включить его в файлы макета, вот так.

1
2
3
4
5
6
7
<fragment
   android:id=»@+id/castMiniController»
   android:layout_width=»match_parent»
   android:layout_height=»wrap_content»
   android:layout_alignParentBottom=»true»
   android:visibility=»gone»
   class=»com.google.android.gms.cast.framework.media.widget.MiniControllerFragment» />

Когда вы щелкнете по этому элементу в любом месте за пределами переключателя воспроизведения / паузы, откроется ваша ExtendedControllerActivity , предоставляя вашим пользователям легкий доступ к контенту на их телевизоре.

Пример MiniControllerFragment

В этом руководстве вы узнали много нового о новом Google Cast SDK для Android, предоставляемых в нем компонентах пользовательского интерфейса и о том, как создать базовое приложение-приемник для кастинга. То, что вы рассмотрели здесь, поможет вам создавать наиболее распространенные типы приложений для кастинга, хотя Google также предоставляет функции, которые позволят вам быстро создавать игры с поддержкой Cast и пользовательские приложения-получатели .