API android.media.projection
представленные в Android 5 (уровень API 21), позволяют захватывать содержимое дисплея устройства в виде медиапотока, который можно воспроизводить, записывать или транслировать на другие устройства, например телевизоры.
В Android 14 (уровень API 34) реализован общий доступ к экрану приложения, который позволяет пользователям совместно использовать одно окно приложения, а не весь экран устройства, независимо от оконного режима. Совместное использование экрана приложения исключает строку состояния, панель навигации, уведомления и другие элементы системного пользовательского интерфейса из общего дисплея, даже если общий доступ к экрану приложения используется для захвата приложения в полноэкранном режиме. Доступен только контент выбранного приложения.
Совместное использование экрана приложений обеспечивает конфиденциальность пользователей, повышает производительность пользователей и улучшает многозадачность, позволяя пользователям запускать несколько приложений, но ограничивая общий доступ к контенту только одним приложением.
Три представления дисплея
Медиа-проекция захватывает содержимое дисплея устройства или окна приложения, а затем проецирует захваченное изображение на виртуальный дисплей, который отображает изображение на Surface
.
Приложение предоставляет Surface
с помощью MediaRecorder
, SurfaceTexture
или ImageReader
, который использует содержимое захваченного дисплея и позволяет управлять изображениями, отображаемыми на Surface
в режиме реального времени. Вы можете сохранить изображения в виде записи или транслировать их на телевизор или другое устройство.
Реальный дисплей
Начните сеанс медиапроекции, получив токен, который предоставит вашему приложению возможность захватывать содержимое дисплея устройства или окна приложения. Токен представлен экземпляром класса MediaProjection
.
Используйте метод getMediaProjection()
системной службы MediaProjectionManager
, чтобы создать экземпляр MediaProjection
при запуске нового действия. Запустите действие с намерением метода createScreenCaptureIntent()
чтобы указать операцию захвата экрана:
Котлин
val mediaProjectionManager = getSystemService(MediaProjectionManager::class.java) var mediaProjection : MediaProjection
val startMediaProjection = registerForActivityResult( StartActivityForResult() ) { result -> if (result.resultCode == RESULT_OK) { mediaProjection = mediaProjectionManager .getMediaProjection(result.resultCode, result.data!!) } }
startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent())
Ява
final MediaProjectionManager mediaProjectionManager = getSystemService(MediaProjectionManager.class); final MediaProjection[] mediaProjection = new MediaProjection[1];
ActivityResultLauncherstartMediaProjection = registerForActivityResult( new StartActivityForResult(), result -> { if (result.getResultCode() == Activity.RESULT_OK) { mediaProjection[0] = mediaProjectionManager .getMediaProjection(result.getResultCode(), result.getData()); } } );
startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent());
Виртуальный дисплей
Центральным элементом медиапроекции является виртуальный дисплей, который вы создаете, вызывая createVirtualDisplay()
в экземпляре MediaProjection
:
Котлин
virtualDisplay = mediaProjection.createVirtualDisplay( "ScreenCapture", width, height, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, surface, null, null)
Ява
virtualDisplay = mediaProjection.createVirtualDisplay( "ScreenCapture", width, height, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, surface, null, null);
Параметры width
и height
определяют размеры виртуального дисплея. Чтобы получить значения ширины и высоты, используйте API-интерфейсы WindowMetrics
представленные в Android 11 (уровень API 30). (Подробную информацию см. в разделе «Размер медиапроекции» .)
Поверхность
Измените размер проекционной поверхности мультимедиа, чтобы обеспечить вывод в соответствующем разрешении. Сделайте поверхность большой (низкое разрешение) для трансляции экрана на телевизоры или мониторы компьютеров и маленькой (высокое разрешение) для записи с дисплея устройства.
Начиная с Android 12L (уровень API 32), при рендеринге захваченного контента на поверхности система равномерно масштабирует контент, сохраняя соотношение сторон, так что оба размера контента (ширина и высота) равны или меньше соответствующих размеров. размеры поверхности. Захваченный контент затем центрируется на поверхности.
Подход масштабирования Android 12L улучшает трансляцию экрана на телевизоры и другие большие дисплеи за счет максимального увеличения размера изображения на поверхности, обеспечивая при этом правильное соотношение сторон.
Разрешение службы переднего плана
Если ваше приложение предназначено для Android 14 или более поздней версии, манифест приложения должен включать декларацию разрешения для типа службы переднего плана mediaProjection
:
<manifest ...>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<application ...>
<service
android:name=".MyMediaProjectionService"
android:foregroundServiceType="mediaProjection"
android:exported="false">
</service>
</application>
</manifest>
Запустите службу медиапроекции вызовом startForeground()
.
Если вы не укажете тип службы переднего плана в вызове, по умолчанию типом будет побитовое целое число типов служб переднего плана, определенных в манифесте. Если в манифесте не указаны типы служб, система выдает MissingForegroundServiceTypeException
.
Согласие пользователя
Ваше приложение должно запрашивать согласие пользователя перед каждым сеансом медиапроекции. Сеанс — это один вызов метода createVirtualDisplay()
. Токен MediaProjection
необходимо использовать только один раз для совершения вызова.
В Android 14 или более поздней версии метод createVirtualDisplay()
выдает исключение SecurityException
, если ваше приложение выполняет одно из следующих действий:
- Передает экземпляр
Intent
, возвращенный изcreateScreenCaptureIntent()
вgetMediaProjection()
более одного раза. - Вызывает
createVirtualDisplay()
более одного раза в одном и том же экземпляреMediaProjection
.
Размер медиа-проекции
Медиа-проекция может захватывать весь дисплей устройства или окно приложения независимо от оконного режима.
Начальный размер
При полноэкранной проекции мультимедиа ваше приложение должно определять размер экрана устройства. При совместном использовании экрана приложения ваше приложение не сможет определить размер захваченного дисплея, пока пользователь не выберет область захвата. Итак, первоначальный размер любой медиапроекции — это размер экрана устройства.
Используйте метод getMaximumWindowMetrics()
платформы WindowManager
, чтобы вернуть объект WindowMetrics
для экрана устройства, даже если ведущее приложение медиапроекции находится в многооконном режиме и занимает только часть дисплея.
Для совместимости до уровня API 14 используйте метод WindowMetricsCalculator
computeMaximumWindowMetrics()
из библиотеки Jetpack WindowManager
.
Вызовите метод WindowMetrics
getBounds()
чтобы получить ширину и высоту дисплея устройства.
Изменения размера
Размер проекции мультимедиа может измениться, когда устройство поворачивается или пользователь выбирает окно приложения в качестве области захвата при совместном использовании экрана приложения. Медиа-проекция может иметь почтовый ящик, если размер захваченного контента отличается от максимальных показателей окна, полученных при настройке медиа-проекции.
Чтобы гарантировать, что проекция мультимедиа точно соответствует размеру захваченного контента для любой захваченной области и для всех поворотов устройства, используйте обратный вызов onCapturedContentResize()
чтобы изменить размер захвата. (Для получения дополнительной информации см. раздел «Настройка» ниже).
Кастомизация
Ваше приложение может настроить пользовательский интерфейс медиапроекции с помощью следующих API MediaProjection.Callback
:
onCapturedContentVisibilityChanged()
: позволяет ведущему приложению (приложению, которое запустило проекцию мультимедиа) показывать или скрывать общий контент.Используйте этот обратный вызов, чтобы настроить пользовательский интерфейс вашего приложения в зависимости от того, видна ли захваченная область пользователю. Например, если ваше приложение видимо для пользователя и отображает захваченное содержимое в пользовательском интерфейсе приложения, а захваченное приложение также видно пользователю (как указано в этом обратном вызове), пользователь видит одно и то же содержимое дважды. Используйте обратный вызов, чтобы обновить пользовательский интерфейс вашего приложения, чтобы скрыть захваченный контент и освободить место макета в вашем приложении для другого контента.
onCapturedContentResize()
: позволяет ведущему приложению изменять размер проекции мультимедиа на виртуальном дисплее иSurface
проекции мультимедиа в зависимости от размера захваченной области отображения.Срабатывает всякий раз, когда захваченный контент — одно окно приложения или полное отображение устройства — меняет размер (из-за поворота устройства или перехода захваченного приложения в другой оконный режим). Используйте этот API, чтобы изменить размер виртуального дисплея и поверхности, чтобы соотношение сторон соответствовало захваченному содержимому, а захват не сохранялся в почтовом ящике.
Восстановление ресурсов
Ваше приложение должно зарегистрировать обратный вызов MediaProjection
onStop()
чтобы получать информацию, когда сеанс медиапроекции остановлен и становится недействительным. Когда сеанс остановлен, ваше приложение должно освободить имеющиеся у него ресурсы, такие как виртуальный дисплей и проекционная поверхность. Остановленный сеанс медиапроекции больше не может создавать новый виртуальный дисплей, даже если ваше приложение ранее не создавало виртуальный дисплей для этой медиапроекции.
Система вызывает обратный вызов, когда медиапроекция завершается. Такое прекращение может произойти по нескольким причинам, например:
- пользователь останавливает сеанс, используя пользовательский интерфейс приложения или чип строки состояния мультимедийной проекции системы.
- экран блокируется
- начинается еще один сеанс медиапроекции
- процесс приложения убит
Если ваше приложение не регистрирует обратный вызов, любой вызов createVirtualDisplay()
выдает IllegalStateException
.
Уклоняться
Android 14 или более поздней версии по умолчанию включает совместное использование экрана приложения. Каждый сеанс медиапроекции дает пользователям возможность поделиться окном приложения или всем дисплеем.
Ваше приложение может отказаться от совместного использования экрана приложения, вызвав метод createScreenCaptureIntent(MediaProjectionConfig)
с аргументом MediaProjectionConfig
возвращаемым в результате вызова метода createConfigForDefaultDisplay()
.
Вызов createScreenCaptureIntent(MediaProjectionConfig)
с аргументом MediaProjectionConfig
возвращенным в результате вызова createConfigForUserChoice()
аналогичен поведению по умолчанию, то есть вызову createScreenCaptureIntent()
.
Приложения с изменяемым размером
Всегда делайте приложения для медиапроекции изменяемого размера ( resizeableActivity="true"
). Приложения с изменяемым размером поддерживают изменение конфигурации устройства и многооконный режим (см. Поддержка многооконного режима ).
Если размер вашего приложения не подлежит изменению, оно должно запросить границы отображения из контекста окна и использовать getMaximumWindowMetrics()
для получения WindowMetrics
максимальной области отображения, доступной приложению:
Котлин
val windowContext = context.createWindowContext(context.display!!, WindowManager.LayoutParams.TYPE_APPLICATION, null) val projectionMetrics = windowContext.getSystemService(WindowManager::class.java) .maximumWindowMetrics
Ява
Context windowContext = context.createWindowContext(context.getDisplay(), WindowManager.LayoutParams.TYPE_APPLICATION, null); WindowMetrics projectionMetrics = windowContext.getSystemService(WindowManager.class) .getMaximumWindowMetrics();
Чип строки состояния и автоматическая остановка
Эксплойты проекции экрана раскрывают личные данные пользователей, такие как финансовая информация, поскольку пользователи не осознают, что экран их устройства используется совместно.
Android 15 (уровень API 35) и более поздних версий отображает большую и заметную строку состояния, предупреждающую пользователей о любом текущем проецировании экрана. Пользователи могут коснуться чипа, чтобы запретить совместное использование, трансляцию или запись своего экрана. Кроме того, проецирование экрана автоматически прекращается, когда экран устройства заблокирован.
Проверьте доступность чипа строки состояния медиапроекции, запустив демонстрацию экрана, трансляцию или запись. Чип должен появиться в строке состояния.
Чтобы ваше приложение высвобождало ресурсы и обновляло свой пользовательский интерфейс, когда проецирование экрана прекращается из-за взаимодействия пользователя с чипом строки состояния или активации экрана блокировки, выполните следующие действия:
Создайте экземпляр
MediaProjection.Callback
.Реализуйте метод обратного вызова
onStop()
. Метод вызывается, когда проецирование экрана прекращается. Освободите все ресурсы, которые хранит ваше приложение, и при необходимости обновите пользовательский интерфейс приложения.
Чтобы проверить обратный вызов, коснитесь чипа строки состояния или заблокируйте экран устройства, чтобы остановить проецирование экрана. Убедитесь, что метод onStop()
вызывается и ваше приложение реагирует должным образом.
Дополнительные ресурсы
Дополнительные сведения о проецировании мультимедиа см. в разделе Захват видео и воспроизведение звука .