Бывют такие не типичные задачи, когда нужно на пример записывать экран смартфона, то что там происходит, паузить, резюмить и останавливать запись. Записывать это все в видео файл, и дальше уже выполнять над ним какие-то действия.
Данный функционал требует нескольких этапов для старта записи. Первый этап — это старт сервиса который проверяет есть ли перманентный доступ на запись, если его нету, то мы переходим ко второму этапу — это запрашиваем доступ на запись, если пользователь дает доступ на запись, тогда мы переходим на третий этап — мы начинаем запись. Это такой небольшой алгоритм который будет при вызове метода startRecording.
Наше приложение будет представлять из себя один экран с кнопкой по нажатию на которую мы будем запускать наш сервис по записи экрана, после повторной записи мы будем останавливать запись и отображать нотфикейшн который будет по клику запускать видео что бы его можно было просмотреть.
Наш сервис будет сам по себе отдельным модулем который можно просто перенести в любой другой проект, и он будет независимо выполнять свою функцию. Для начала нам нужно будет создать сервис который мы будем вызывать из хелпера, котороый в свою очередь будет вызывать диалог с вопросом можно ли записывать экран, и после получения разрешения начнет записывать. Так же можно расширить функционал и добавить сервис с плавающей кнопкой по нажатию на которую будет останавливаться запись или какие-то другие действия.
Вот такой у нас будет флоу действия. Теперь можно приступать к разработке. И начнем мы с сервиса который будет производить запись экрана. Но для начала нам нужно создать несколько листенеров которые будут кидать колбеки в хелперов и получать их обратно.
Давайте посмотрим какаие библиотеки мы будем использовать в gradle файле.
app/build.gradle
Всего лишь два, как оказалось, ой как же это удивительно.
— appcompat — нужен для использования всех прелестей androidX.
— dexter — для того что бы спросить пользователя дать пермишены на запись экрана и запись и тение файлов.
Дальше нам нужно будет создать пару листенеров которые нам будут нужны чуть папизже.
RecordingPermissionListener.kt
RecordListener.kt
StopRecordingListener.kt
Эти три интерфейса будут встречаться нам очень часто в этой статье, они будут использоваться почти в каждом классе который относится к записи экрана. Они будут передавать в наши хелперы и менеджеры нужные состояния.
RecordService.kt
Данный сервис довольно типичный. Нас интересует в данном сервисе один класс и один метод.
— onStartCommand — выполняет запуск старта записи и остановки записи.
— RecordServiceBinder — класс через который будет выполнятся запуск или останвока записи, его мы будем вызывать из другого класса-хелпера который будет оборачивать в себе все нужные функции для старта и стопа записи.
Остальные методы выполняют функционал создания записи или остановки, вызов диалога с запросом на запись и так далее… Названия говорят сами за себя.
Далее нам нужно создать активити которая будет показывать диалог с вопросом можно ли записывать.
RequestMediaProjectionActivity.kt
Здесь нас интересует так же пара методов, остальные нуны чисто что бы эти пара методов жили так как мы хотим.
— createIntent — метод который вызывает RequestMediaProjectionActivity и задает параметры по которым мы дальше будем создавать видео-файл и передаем код для onActivityResult
— onActivityResult в свою очередь запускает сервис через метод который задает путь файла для записи.
— requestScreenRecordPermission — запрашивает разрашение у пользователя на запись экрана.
Далее нам нужно создать класс который будет создавать инстанс MediaRecorder и работать с этим инстансом для создания собственно видео-файла и последующей записи в него.
State.kt
Но для начала нам нужно создать енам с нужными нам параметрами для сохранения стейтов записи. Создали, а дальше уже смотрим что из себя представляет наш ScreenRecorder.
ScreenRecorder.kt
В инициализации мы задаем ширину экрана какую мы хотим записывать, собствнно берем всю ширину экрана возможную и так же указываем качество которое мы хотим что бы было у записанного видео. Видео будет с шириной 1200 пикселей и с высотой в 720 пикселей, битрейт (частоту записи видео) мы задали 1600000 бит / секунду и фрейм рейт (колиество кадров в секунду) мы задали в 24 к / с.
— initMediaRecorder — этот метод у нас инициализирует и задает важные параметры такие как адрес куда будет писаться видео, с каким качеством, битрейтом, фрейм рейтом и так далее…
— startRecord — у нас создает MediaRecorder, инициализирует его и стартует запись экрана. И в конце ставим стейт что у нас видео записывается.
— stopRecord — останавливаем запись, и ставим стейт что у нас рекордер проинициализирован.
— setUpVirtualDisplay — задает ширину и высоту и создает с ней VirtualDisplay который позволит записать экран.
— shareScreen — начинает запись экрана.
Далее мы создадим отдельный хелпер который будет расширять возможности нашего ScreenRecorder'a, так как сам собственно класс рекордера у нас включает в себя инициализацию и старт / стоп записи. А вот в хелпере мы будем запрашивать возможность записи у пользователя и записывать или не записывать видео в зависимости от решения пользователя.
ScreenRecorderHelper.kt
Собственно данный класс повторяет функционал ScreenRecorder, но только в этом классе мы добавили запрос с помощью MediaProjectionManager который спрашивает у пользователя «хотиш писать видос или не хотиш?» и пользователь уже решает хотит он или нет.
Прям все методы я не буду описывать, опишу только пару, в основном данный класс нужен для индикации о том что происходит на экране. Это будут startRecording и stopRecording, два метода которые нам возвращает лисенер из ScreenRecorder, по приходу колбека в эти методы мы отправляем запрос в сервис что бы он проверил все ли ок, хотит пользователь писать или нет, и если хотит — тогда мы пишем в файл, и отправляем колбек в onStartRecording в сервисе который отправляет колбек onPermissionGranted в MainActivity, в которой уже мы будем показывать какой-то индикатор что запись началась. Если же пользователь отказал — то мы отправляем другой колбек onPermissionDenied в MainActivity. И если запись была остановленная то так же отправляется колбек в MainActivity для индикации пользователю.
Все очень сложно и запутанно, я если честно сам офигел, но после пары часов разбора кода я думаю все встанет на свои места у человека которому это нужно :)
Но это еще не все, у нас еще будет класс менеджер который будет в себе обсорбировать все вызовы сервиса, старт записи, стоп записи и паузы / резюма. Он нам нужен для разделения логики работы что бы вот совсем было просто работать с нашим рекордером, что бы было достаточно создать инстанс RecorderManager и вызвать нужный метод и получить результат, какой-то колбек и еще видео ко всему.
RecorderManager.kt
Здесь мы создали инстанс RecordServiceBinder который нам нужен для проверки есть ли у нас на данный момент запись или нет, если RecordServiceBinder = null тогда мы создаем новый, а если не null — то мы запускаем / останавливаем запись.
— onServiceConnected — биндер который запускается сервис запущен и готов записывать, то есть когда мы даем добро на запись экрана.
— bindRecordService — нужен для старта сервиса, мы по сути инициализируем сервис и запускаем запрос на запись видео. Дальше как мы получили какой-то результат, пользователь дал доступ на запись или нет, мы вызываем onServiceConnected который запускает запись.
— unbindRecordService — вызывается в основном когда нам нужно закончить запись и остановить сервис. Его мы будем вызывать в стоп рекординге, когда нам сервис уже не нужен.
— startRecording и stopRecording — проверяют есть ли у нас запись и если нету то создает сервис и начинает ее, и соответственно если запись идет то оно останавливает ее, и все это сопровождается лисенерами которые отправляют колбеки в активити.
— pauseRecording и resumeRecording — останавливает или резюмит запись, по сути тоже самое что и старт стоп, но в случае с паузой и резюмом, нам придется писать в новый файл каждый раз когда мы останавливаем или стартуем запись, дозаписывать в тот же не выйдет.
Далее нам нужен класс в котором у нас будет создаваться Notification по клику на который мы будем открывать видео.
NotificationWrapper.kt
Просто создаем нотификейшн, в который передаем адрес файла для воспроизведения, и передаем его в интент который будет открывать видео. Так же мы задали канал для андроид 6 и выше что бы нотиф мог отображаться на них. Ну и далее уже типичное создаем NotificationCompat и задаем параметры отображения.
activity_main.xml
Просто кнопка, мы без излишеств. Скромные маленькие человечки.
MainActivity.kt
В onCreate мы спрашиваем у пользователя разрешения на запись экрана и на запись в внутренее хранилище. Так же вешаем лисенера на кнопку по нажатию на которую мы стартуем или стопаем запись экрана. Создаем инстанс RecorderManager и подписываемся на всех лисенеров на которые нас просит этот класс пописаться.
— getVideoFilePath — создает путь к файлу и сам файл в который мы будем писать.
Ну и дальше уже просто создаем кучу методов которые просит нас создать студия, это методы из наших лисеноров.
В onStopRecording мы показываем нотиф с помощью нашего класса NotificationWrapper по окончанию записи…
AndroidManifest.xml
Так же еще осталось прописать все пермишенны в AndroidManifest, сервис и дополнительную активити которая будет вызываться для запроса на запись при клике на кнопку старта. Прописываем те же самые пермишены которые запрашивали в MainActivity.
Вот собственно и вся магия, надеюсь что хоть немного понятно объяснил весь этот треш который тут происходит. И это кому-то пригодится.
Исходники:
GitHub
Данный функционал требует нескольких этапов для старта записи. Первый этап — это старт сервиса который проверяет есть ли перманентный доступ на запись, если его нету, то мы переходим ко второму этапу — это запрашиваем доступ на запись, если пользователь дает доступ на запись, тогда мы переходим на третий этап — мы начинаем запись. Это такой небольшой алгоритм который будет при вызове метода startRecording.
Наше приложение будет представлять из себя один экран с кнопкой по нажатию на которую мы будем запускать наш сервис по записи экрана, после повторной записи мы будем останавливать запись и отображать нотфикейшн который будет по клику запускать видео что бы его можно было просмотреть.
Наш сервис будет сам по себе отдельным модулем который можно просто перенести в любой другой проект, и он будет независимо выполнять свою функцию. Для начала нам нужно будет создать сервис который мы будем вызывать из хелпера, котороый в свою очередь будет вызывать диалог с вопросом можно ли записывать экран, и после получения разрешения начнет записывать. Так же можно расширить функционал и добавить сервис с плавающей кнопкой по нажатию на которую будет останавливаться запись или какие-то другие действия.
Вот такой у нас будет флоу действия. Теперь можно приступать к разработке. И начнем мы с сервиса который будет производить запись экрана. Но для начала нам нужно создать несколько листенеров которые будут кидать колбеки в хелперов и получать их обратно.
Давайте посмотрим какаие библиотеки мы будем использовать в gradle файле.
app/build.gradle
Всего лишь два, как оказалось, ой как же это удивительно.
— appcompat — нужен для использования всех прелестей androidX.
— dexter — для того что бы спросить пользователя дать пермишены на запись экрана и запись и тение файлов.
Дальше нам нужно будет создать пару листенеров которые нам будут нужны чуть папизже.
RecordingPermissionListener.kt
RecordListener.kt
StopRecordingListener.kt
Эти три интерфейса будут встречаться нам очень часто в этой статье, они будут использоваться почти в каждом классе который относится к записи экрана. Они будут передавать в наши хелперы и менеджеры нужные состояния.
RecordService.kt
Данный сервис довольно типичный. Нас интересует в данном сервисе один класс и один метод.
— onStartCommand — выполняет запуск старта записи и остановки записи.
— RecordServiceBinder — класс через который будет выполнятся запуск или останвока записи, его мы будем вызывать из другого класса-хелпера который будет оборачивать в себе все нужные функции для старта и стопа записи.
Остальные методы выполняют функционал создания записи или остановки, вызов диалога с запросом на запись и так далее… Названия говорят сами за себя.
Далее нам нужно создать активити которая будет показывать диалог с вопросом можно ли записывать.
RequestMediaProjectionActivity.kt
Здесь нас интересует так же пара методов, остальные нуны чисто что бы эти пара методов жили так как мы хотим.
— createIntent — метод который вызывает RequestMediaProjectionActivity и задает параметры по которым мы дальше будем создавать видео-файл и передаем код для onActivityResult
— onActivityResult в свою очередь запускает сервис через метод который задает путь файла для записи.
— requestScreenRecordPermission — запрашивает разрашение у пользователя на запись экрана.
Далее нам нужно создать класс который будет создавать инстанс MediaRecorder и работать с этим инстансом для создания собственно видео-файла и последующей записи в него.
State.kt
Но для начала нам нужно создать енам с нужными нам параметрами для сохранения стейтов записи. Создали, а дальше уже смотрим что из себя представляет наш ScreenRecorder.
ScreenRecorder.kt
В инициализации мы задаем ширину экрана какую мы хотим записывать, собствнно берем всю ширину экрана возможную и так же указываем качество которое мы хотим что бы было у записанного видео. Видео будет с шириной 1200 пикселей и с высотой в 720 пикселей, битрейт (частоту записи видео) мы задали 1600000 бит / секунду и фрейм рейт (колиество кадров в секунду) мы задали в 24 к / с.
— initMediaRecorder — этот метод у нас инициализирует и задает важные параметры такие как адрес куда будет писаться видео, с каким качеством, битрейтом, фрейм рейтом и так далее…
— startRecord — у нас создает MediaRecorder, инициализирует его и стартует запись экрана. И в конце ставим стейт что у нас видео записывается.
— stopRecord — останавливаем запись, и ставим стейт что у нас рекордер проинициализирован.
— setUpVirtualDisplay — задает ширину и высоту и создает с ней VirtualDisplay который позволит записать экран.
— shareScreen — начинает запись экрана.
Далее мы создадим отдельный хелпер который будет расширять возможности нашего ScreenRecorder'a, так как сам собственно класс рекордера у нас включает в себя инициализацию и старт / стоп записи. А вот в хелпере мы будем запрашивать возможность записи у пользователя и записывать или не записывать видео в зависимости от решения пользователя.
ScreenRecorderHelper.kt
Собственно данный класс повторяет функционал ScreenRecorder, но только в этом классе мы добавили запрос с помощью MediaProjectionManager который спрашивает у пользователя «хотиш писать видос или не хотиш?» и пользователь уже решает хотит он или нет.
Прям все методы я не буду описывать, опишу только пару, в основном данный класс нужен для индикации о том что происходит на экране. Это будут startRecording и stopRecording, два метода которые нам возвращает лисенер из ScreenRecorder, по приходу колбека в эти методы мы отправляем запрос в сервис что бы он проверил все ли ок, хотит пользователь писать или нет, и если хотит — тогда мы пишем в файл, и отправляем колбек в onStartRecording в сервисе который отправляет колбек onPermissionGranted в MainActivity, в которой уже мы будем показывать какой-то индикатор что запись началась. Если же пользователь отказал — то мы отправляем другой колбек onPermissionDenied в MainActivity. И если запись была остановленная то так же отправляется колбек в MainActivity для индикации пользователю.
Все очень сложно и запутанно, я если честно сам офигел, но после пары часов разбора кода я думаю все встанет на свои места у человека которому это нужно :)
Но это еще не все, у нас еще будет класс менеджер который будет в себе обсорбировать все вызовы сервиса, старт записи, стоп записи и паузы / резюма. Он нам нужен для разделения логики работы что бы вот совсем было просто работать с нашим рекордером, что бы было достаточно создать инстанс RecorderManager и вызвать нужный метод и получить результат, какой-то колбек и еще видео ко всему.
RecorderManager.kt
Здесь мы создали инстанс RecordServiceBinder который нам нужен для проверки есть ли у нас на данный момент запись или нет, если RecordServiceBinder = null тогда мы создаем новый, а если не null — то мы запускаем / останавливаем запись.
— onServiceConnected — биндер который запускается сервис запущен и готов записывать, то есть когда мы даем добро на запись экрана.
— bindRecordService — нужен для старта сервиса, мы по сути инициализируем сервис и запускаем запрос на запись видео. Дальше как мы получили какой-то результат, пользователь дал доступ на запись или нет, мы вызываем onServiceConnected который запускает запись.
— unbindRecordService — вызывается в основном когда нам нужно закончить запись и остановить сервис. Его мы будем вызывать в стоп рекординге, когда нам сервис уже не нужен.
— startRecording и stopRecording — проверяют есть ли у нас запись и если нету то создает сервис и начинает ее, и соответственно если запись идет то оно останавливает ее, и все это сопровождается лисенерами которые отправляют колбеки в активити.
— pauseRecording и resumeRecording — останавливает или резюмит запись, по сути тоже самое что и старт стоп, но в случае с паузой и резюмом, нам придется писать в новый файл каждый раз когда мы останавливаем или стартуем запись, дозаписывать в тот же не выйдет.
Далее нам нужен класс в котором у нас будет создаваться Notification по клику на который мы будем открывать видео.
NotificationWrapper.kt
Просто создаем нотификейшн, в который передаем адрес файла для воспроизведения, и передаем его в интент который будет открывать видео. Так же мы задали канал для андроид 6 и выше что бы нотиф мог отображаться на них. Ну и далее уже типичное создаем NotificationCompat и задаем параметры отображения.
activity_main.xml
Просто кнопка, мы без излишеств. Скромные маленькие человечки.
MainActivity.kt
В onCreate мы спрашиваем у пользователя разрешения на запись экрана и на запись в внутренее хранилище. Так же вешаем лисенера на кнопку по нажатию на которую мы стартуем или стопаем запись экрана. Создаем инстанс RecorderManager и подписываемся на всех лисенеров на которые нас просит этот класс пописаться.
— getVideoFilePath — создает путь к файлу и сам файл в который мы будем писать.
Ну и дальше уже просто создаем кучу методов которые просит нас создать студия, это методы из наших лисеноров.
В onStopRecording мы показываем нотиф с помощью нашего класса NotificationWrapper по окончанию записи…
AndroidManifest.xml
Так же еще осталось прописать все пермишенны в AndroidManifest, сервис и дополнительную активити которая будет вызываться для запроса на запись при клике на кнопку старта. Прописываем те же самые пермишены которые запрашивали в MainActivity.
Вот собственно и вся магия, надеюсь что хоть немного понятно объяснил весь этот треш который тут происходит. И это кому-то пригодится.
Исходники:
GitHub
Комментариев нет:
Отправить комментарий