Можно долго спорить на тему, стоит ли давать маленьким детям доступ к планшетам и смартфонам. Кто-то говорит что это вредно для глаз или для психики, кто-то — что родителям надо бы самим играть и читать с детьми, а не пытаться отгородится от них гаджетами. Что характерно, чаще всего такое говорят люди, у которых своих детей нет. И которые не знают, какое это блаженство — когда чадо замолкает хотя бы на полчаса, перестает крушить все вокруг, спокойненько лежит на диване и смотрит мультики. Есть и еще один аргумент — дети чутко все повторяют за родителями, если родители непрерывно сидят уткнувшись в телефон, то очень сложно объяснить детям, почему родителям можно, а им — нет.
В общем можете закидывать меня тухлыми помидорами, но мой сын, которому сейчас без пары дней три года, время от времени лежит с планшетом на диване и смотрит мульики на ютубе.
К сожалению, я очень быстро уяснил, что детский ютуб — это просто АДОВЫЙ ТРЕШ. Про это даже на Хабре уже был перевод статьи. Детские каналы — это какие-то бездонные клоаки, наполненные вырвиглазными видео типа "разворачиваем сто киндер-сюрпризов", "дурацкие стишки с убогой 3д графикой под крикливую музыку" и "гоняем машинки в Beam NG под дурацкие комментарии". По какой-то причине все это является очень привлекательным для маленьких детей, которые бросаются кликать на такие видео как только увидят их в рекомендованных. А YouTube не позволяет управлять рекомендациями. Даже дав своему сыну планшет с включенными "нормальными" мультиками, я уже через пару минут наблюдаю, как он за два клика по рекомендациям опять находит эти чертовы шоколадные яйца и снова начинает в них залипать.
Все, с меня хватит, решил я. И начал пилить свое приложение для просмотра ютуба, позволяющее выбрать список каналов и показывающее только видео с этих каналов. Как я это делал — под катом.
Вот вам один пример трешевидео, с которыми мы будем бороться. Хреново сделанная бессмысленная 3д анимация под дурацкую музыку — и 142 миллиона просмотров. Почему-то такие видео детей просто зомбируют, они готовы их пересматривать и пересматривать.
Для сравнения масштабов — крутейший ролик где Крис Хэдфилд поет под гитару на МКС имеет всего 40 миллионов просмотров, а замечательный клип OK GO с настоящей невесомостью (никаких тросов и хромакея, только честная невесомость на борту специального самолета) — всего 12 миллионов, в десять раз меньше.
Кстати, рекламодатели на ютубе и сами не рады популярности детских каналов, так как показ рекламы на них — пустая трата бюджетов. Дети смотрят рекламу, иногда даже кликают, но ничего не покупают, а миллионы просмотров быстро расходуют бюджет.
Постановка задачи
Сразу скажу, что я не ставил задачу сделать непробиваемое для детей приложение. Такие попытки уже есть, в том числе и тут на хабре — там режут все подряд, чтобы ребенок не мог выйти из приложения, купить что-то, открыть браузер и т.п. Есть и варианты с хардверными блокировками, отключенными портами, кастомными оболочками андроида и прочими сложностями (например вот статья за 2012 год, проект вроде все еще жив). Но это все имеет смысл для детей постарше, которые уже во всю исследуют возможности своих гаджетов. Для детей же 2-3 лет никакая особая защита не нужна, их вполне устраивает смотреть видео и время от времени тыкать в превьюшки других роликов. Главное, чтобы эти ролики были какими надо, а не какие предложит YouTube (а предложит он нескончаемые шоколадные яйца и baby fingers).
Из этого родился список требований:
- Настраиваемый список каналов
- Защита его от изменений (скорее от случайных, ибо у хитрых взрослых детей уже есть куча способов вызнавания паролей родителей)
- Само приложение — похоже на ютубовское, слева играет видео, справа колонка превьюшек, на которые можно нажимать
- Основной use-case — случайное тыкание по видео, ребенку в общем-то все равно что именно смотреть
При этом работа приложения состоит из двух основных частей: поис и настройка каналов с помощью YouTube Data API и затем воспроизведение видео. Причем вариантов проигрывания видео внутри собственного приложения я на данный момент обнаружил два:
YouTube Android Player API
https://developers.google.com/youtube/android/player/
Это официальный способ проигрывать видео в своем андроид приложении. Для его работы на девайсе пользователя уже должно быть установлено официальное приложение YouTube, которое в своем составе включает некую службу, которую можно использовать из других приложений. Так что этот самый Player API представляет из себя маленькую библиотеку, взаимодействующую с отдельно устанавливаемым приложением.
Сперва я воспользовался им, но после некоторого времени использования выяснилось, что у него есть критичная проблема. В нем нельзя толком кастомизировать внешний вид плеера, в частности управлять кнопками (можно только спрятать вообще все целиком, но тогда лишишься и кнопки полноэкранного режима). А на панели управления плеером есть нехорошая кнопочка "перейти к просмотру на YouTube", которая открывает официальное приложение (которое обязательно должно быть на девайсе), в котром уже нет никакой фильтрации. И дети легко (случайно или нет) нажимают на нее, переходят к нефильтрованному приложению, и через неколько минут я уже снова слышу "baby finger, baby finger where are you" и вижу какие-то вырвиглазные трешеанимации на экране.
Еще немножко треша, который дети находят за пару кликов. Больше 900 миллионов просмотров!
Поэтому от официального плеера я отказался, найдя ему замену в виде следующей библиотеки.
Android-YouTube-Player
https://github.com/PierfrancescoSoffritti/android-youtube-player
Библиотечка похоже представляет собой обертку вокруг WebView, управляющую веб-плеером через его JavaScript API. Из плюсов — возможность полной кастомизации интерфейса.
Инициализация плеера слегка неочевидна, особенно после перехода с официальной библиотеки:
- Получаем нашу вьюху класса com.pierfrancescosoffritti.androidyoutubeplayer.player.YouTubePlayerView
- Зовем ей initialize(), передавая листенер
- У листенера вызывается метод onInitSuccess(@Nonnull final YouTubePlayer youTubePlayer), получающий экземпляр объекта плеера. Это единственный способ его получить. До этого момента процесс инициализации был идентичен таковому у официального клиента
- У плеера вызываем метод addListener(), передавая в него еще один листенер (больше листенеров богу листенеров!)
- У этого листенера вызывается метод onReady() — только после этого момента можно загружать и показывать видео и пользоваться плеером. Если по ошибке попробовать что-то загрузить в onInitSuccess то библиотека начнет плеваться странными ошибками.
Управлять жизненным циклом плеера (остановка воспроизведения в onPause() и все в таком духе) можно вручную, а можно зарегистрировать нашу вьюху как Lifecycle Observer (для чего наша Activity должна наследоваться от AppCompatActivity). В таком случае библиотека всю рутину возьмет на себя.
Еще один странный нюанс библиотеки — отсутствие поддержки полноэкранного режима из коробки. Кнопочка для него по умолчанию есть в плеере, но не делает ничего. По сути полноэкранный режим приходится делать вручную — прятать гуй, разворачивать окно плеера на весь экран, а затем все восстанавливать обратно. Это может оказаться достаточно нетривиальным, если у вас какой-то сложный интерфейс вокруг плеера. У меня же там был только список видео, который легко прячется вручную.
В сэмплах библиотеки есть класс FullScreenHelper, из которого можно взять нужный код. Выглядит его использование как-то так:
youTubePlayerView.addFullScreenListener(new YouTubePlayerFullScreenListener() {
private final View rootLayout = findViewById(R.id.rootLayout);
@Override
public void onYouTubePlayerEnterFullScreen() {
fullScreenHelper.enterFullScreen();
rootLayout.setPadding(0, 0, 0, 0);
}
@Override
public void onYouTubePlayerExitFullScreen() {
fullScreenHelper.exitFullScreen();
rootLayout.setPadding(8, 8, 8, 8);
youTubePlayerView.getLayoutParams().height = LinearLayout.LayoutParams.MATCH_PARENT;
}
});
FullScreenHelper прячет лишний интерфейс и ставит флаги отображения экрана. Пришлось еще подхачить выставление отступов (константы в коде зло, я знаю) и еще почему-то высота при выходе из фуллскрина сбивалась, если вручную ее не поправить.
Вообще как-то много минусов получилось у этой библиотеки, но все перекрывает главный плюс — возможность настраивать интерфейс проигрывателя. Мне было достаточно спрятать кнопку "показать на YouTube", сохранив при этом полноэкранный режим:
youTubePlayerView.getPlayerUIController().showFullscreenButton(true);
youTubePlayerView.getPlayerUIController().showYouTubeButton(false);
А вообще там можно управлять всеми кнопками, отображением текущего времени, можно добавлять свои собственне View и т.п. — в итоге по возможностям кастомизации эта библиотека оставляет своего официального конкурента далеко позади.
Ну и после работы по настройке плеера новое видео грузится просто как youTubePlayer.loadVideo(url, startTime);
Получение списков видео, работа с YouTube Data API
URL видео надо еще откуда-то получить. К счастью у YouTube есть богатое API, позволяющее делать всякие поисковые запросы и получать сведения о каналах. Чтобы начать его использовать нужно подключить его в своей Developer Console и получить ключ.
Для работы приложения нам нужны две функции: поиск каналов и получение списка видео для данного канала. Поиск выглядит как-то так, мы хотим искать каналы и плейлисты по запросу "Peppa Pig". Нас интересует id (чтобы в дальнейшем скачивать список видео для канала по его id) и snippet в нем содержатся основные данные о сущности: название, картинка-превью, описание.
YouTube.Search.List searchListByKeywordRequest = youTube.search().list("snippet,id");
searchListByKeywordRequest.setMaxResults(10L);
searchListByKeywordRequest.setQ("Peppa Pig");
searchListByKeywordRequest.setType("channel,playlist");
searchListByKeywordRequest.setKey("<api key>");
SearchListResponse response = searchListByKeywordRequest.execute();
Важно правильно задавать список тех частей объекта (в данном случае это snippet и id) которые мы хотим получать. YouTube API использует довольно сложную систему лимитирования запросов: по умолчанию ежедневная квота составляет миллион единиц, но при этом один запрос может потреблять много единиц. Больше всего потребляют запросы записывающие что-то (заливающие видео, или меняющие плейлисты), но читающие запросы могут тоже потреблять немало. Запросы на чтение данных потребляют одну единицу квоты сами по себе, плюс по две единицы на каждый фрагмент данных. Т.е. например вот этот вот запрос что указан выше потребляет 5 единиц (одну за сам запрос и по 2 за фрагменты sinppet и id). А полный запрос всех данных о канале, у которого 6 фрагментов, съел бы 13. Для видео доступны 10 фрагментов, так что полный запрос съест 21 единицу квоты. Поэтому имеет смысл запрашивать только то, что вам нужно.
Запрос на получение списка видео из канала выглядит вот так:
YouTube.Search.List request = youTube.search().list("snippet,id");
request.setChannelId("<channel id>");
request.setType("video");
request.setMaxResults(50L);
request.setKey("<api key>");
SearchListResponse response = request.execute();
Несколько неочевидно сделано разбиение ответа на страницы, вместо того чтобы явно указывать номер страницы и количество результатов, в ответе может присутствовать параметр nextPageToken, если он есть — значит впереди еще есть страницы, надо поместить этот параметр в объект запроса и выполнить запрос еще раз.
if (response.getNextPageToken() != null) {
request.setPageToken(response.getNextPageToken());
response = request.execute();
}
В целом API хорошо документировано, есть готовые примеры кода на многих языках, примеры запросов для запуска в браузере и т.п.
Опыт использования
После того, как первая версия была готова, началось тестирование. Сперва на своей семье, затем я выложил ее в Google Play и начал собирать фидбек от пользователей. Сразу выяснились нюансы:
- Кроме каналов понадобились и плейлисты. Так как на одном канале зачастую нормальные видео совмещались с адовым трешем. Например на "Теремок ТВ" есть неплохие мультики про царевну и всякая вырвиглазная хрень ("бибика" или "доктор машинкова"), из-за которых добавлять канал целиком не хотелось.
- А потом — еще и отдельные видео. Например есть каналы, где есть подборки типа "все серии мультсериала в одном видео", и опять же хочется выбрать отдельные видео, а не канал целиком.
- Понадобился возврат назад к предыдущему видео. Ибо истерика ребенка, который случайно ткнул пальцем в следующе видео и уже не может вернуться назад к тому, которое только что смотрел — это не шутки.
- Сейчас я понял что нужен еще и поиск. Потому что "хочу мультик про машинки и приведение", а поиска пока в приложении нет, а тыкать пальцем в надежде что рандом таки выдаст этот чертов мультик можно долго.
- Про отключение кнопки просмотра на ютубе я уже выше писал.
- Еще хочется прикрутить Pinned Mode — начиная с пятого андроида можно сделать так, что чтобы свернуть приложение надо было нажать и подержать несколько кнопок сразу.
Если идея кому-то приглянулась — https://play.google.com/store/apps/details?id=ru.sundogs.youtubekiosk В бесплатной версии можно добавить только три канала, пишите в личку — дам ключ на снятие ограничения. А я пока потихоньку допиливаю новые фичи по реквестам, ну и радуюсь что теперь могу быть уверен в качестве того, что мой сын смотрит.
И нет, он не просиживает дни напролет за планшетом, а скорее предпочитает прогулки и активные игры, прятки-догонялки и рисование на всем подряд (от обоев до игрушек), а вечером любит слушать сказки. Планшет и мультфильмы — лишь один из возможных инструментов развлечения ребенка, но при этом один из наиболее сложно управляемых. С чем отчасти и призвано бороться мое приложение.
Автор: JediPhilosopher
Данная программа сделана бесплатной. Код открыт для возможности доделать энтузиастами. Программа “YouTube Channel Whitelist” размещена на 4PDA – https://4pda.ru/forum/index.php?showtopic=954291