Android 5(API レベル 21)で導入された android.media.projection API を使用すると、デバイス ディスプレイのコンテンツをメディア ストリームとしてキャプチャし、テレビなどの他のデバイスで再生、録画、キャストできます。
Android 14(API レベル 34)では、アプリの画面共有が導入されました。これにより、ウィンドウ モードに関係なく、デバイス画面全体ではなく単一のアプリ ウィンドウを共有できます。アプリの画面共有では、アプリの画面共有を使用してアプリを全画面表示でキャプチャする場合でも、ステータスバー、ナビゲーション バー、通知、その他のシステム UI 要素は共有ディスプレイから除外されます。選択したアプリのコンテンツのみが共有されます。
アプリの画面共有により、ユーザーは複数のアプリを実行しながら、コンテンツの共有を 1 つのアプリに制限できるため、ユーザーのプライバシーが保護され、生産性が向上し、マルチタスクが強化されます。
3 つのディスプレイ表現
メディア プロジェクションでは、デバイス ディスプレイまたはアプリ ウィンドウのコンテンツをキャプチャし、
キャプチャした画像を仮想ディスプレイに投影して、
Surfaceにレンダリングします。
Surfaceに書き込む。
アプリは、キャプチャされたディスプレイのコンテンツを使用する MediaRecorder、
SurfaceTexture、または ImageReader によって Surface を提供します。これにより、Surface にレンダリングされた画像を
リアルタイムで管理できます。録画として画像を保存したり、テレビなどのデバイスにキャストしたりできます。
実際のディスプレイ
メディア プロジェクション セッションを開始するには、デバイス ディスプレイまたはアプリ ウィンドウのコンテンツをキャプチャする権限をアプリに付与するトークンを取得します。トークン
は MediaProjection クラスのインスタンスで表されます。
新しい
アクティビティを開始するときに MediaProjection インスタンスを作成するには、MediaProjectionManager
システム サービスの getMediaProjection() メソッドを使用します。
createScreenCaptureIntent() メソッドのインテントを使用してアクティビティを開始し、画面キャプチャ オペレーションを指定します。
Kotlin
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())
Java
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 インスタンスで作成します。
Kotlin
virtualDisplay = mediaProjection.createVirtualDisplay( "ScreenCapture", width, height, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, surface, null, null)
Java
virtualDisplay = mediaProjection.createVirtualDisplay( "ScreenCapture", width, height, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, surface, null, null);
width パラメータと height パラメータは、仮想ディスプレイのサイズを指定します。幅と高さの値を取得するには、WindowMetrics API
を Android 11(API レベル 30)で導入しました。(詳しくは、メディア プロジェクション
サイズをご覧ください)。
Surface
適切な解像度で出力を生成するには、メディア プロジェクション サーフェスのサイズを調整します。テレビやパソコンのモニターに画面をキャストする場合は大きいサイズ(低解像度)に、デバイスのディスプレイを録画する場合は小さいサイズ(高解像度)にします。
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() の 1 回の呼び出しです。呼び出しを行うには、MediaProjection トークンを 1 回だけ使用する必要があります。
Android 14 以降では、createVirtualDisplay() メソッドが
SecurityException をスローします。
Intentから返されたcreateScreenCaptureIntent()インスタンスをgetMediaProjection()に複数回渡す- 同じ
MediaProjectionインスタンスでcreateVirtualDisplay()を複数回呼び出す
メディア プロジェクションのサイズ
メディア プロジェクションは、ウィンドウ モードに関係なく、デバイス ディスプレイ全体またはアプリ ウィンドウをキャプチャできます。
初期サイズ
全画面表示のメディア プロジェクションでは、アプリでデバイス画面のサイズを判断する必要があります。アプリの画面共有では、ユーザーがキャプチャ領域を選択するまで、アプリはキャプチャしたディスプレイのサイズを判断できません。そのため、メディア プロジェクションの初期サイズはデバイス画面のサイズになります。
メディア プロジェクション ホストアプリがマルチ ウィンドウ モードで実行されていてディスプレイの一部だけを占有している場合でも、プラットフォームの WindowManager getMaximumWindowMetrics() メソッドを使用して、デバイス画面の WindowMetrics オブジェクトを返します。
API レベル 14 までの互換性をさかのぼって確保するには、Jetpack WindowManager
ライブラリの WindowMetricsCalculator
computeMaximumWindowMetrics() メソッドを使用します。
WindowMetrics getBounds() メソッドを呼び出して、
デバイス ディスプレイの幅と高さを取得します。
サイズの変更
デバイスが回転したときや、アプリの画面共有でユーザーがアプリ ウィンドウをキャプチャ領域として選択したときに、メディア プロジェクションのサイズが変更されることがあります。キャプチャしたコンテンツのサイズが、メディア プロジェクションの設定時に取得した最大ウィンドウ サイズと異なる場合、メディア プロジェクションがレターボックス表示になることがあります。
キャプチャした領域とデバイスの回転全体で、メディア プロジェクションがキャプチャしたコンテンツのサイズに正確に揃うようにするには、onCapturedContentResize() コールバックを使用してキャプチャのサイズを変更します (詳しくは、次のカスタマイズをご覧ください)。
カスタマイズ
アプリは、次の
MediaProjection.Callback API を使用して、メディア プロジェクションのユーザー エクスペリエンスをカスタマイズできます。
onCapturedContentVisibilityChanged(): ホストアプリ(メディア プロジェクションを開始したアプリ)で共有コンテンツの表示 / 非表示を切り替えることができます。このコールバックを使用して、キャプチャした領域がユーザーに表示されるかどうかに基づいて、アプリの UI をカスタマイズします。たとえば、アプリがユーザーに表示されていて、キャプチャしたコンテンツがアプリの UI 内に表示されている場合、キャプチャしたアプリもユーザーに表示されている(このコールバックで示される)場合、ユーザーには同じコンテンツが 2 回表示されます。このコールバックを使用して、アプリの UI を更新してキャプチャしたコンテンツを非表示にし、アプリのレイアウト スペースを他のコンテンツ用に解放します。
onCapturedContentResize(): ホストアプリで、キャプチャしたディスプレイ領域のサイズに基づいて、仮想ディスプレイとメディア プロジェクションSurfaceのメディア プロジェクションのサイズを変更できます。キャプチャしたコンテンツ(単一のアプリ ウィンドウまたはデバイス ディスプレイ全体)のサイズが変更されるたびにトリガーされます(デバイスの回転や、キャプチャしたアプリが別のウィンドウ モードに移行したため)。この API を使用して、仮想ディスプレイとサーフェスの両方のサイズを変更し、アスペクト比がキャプチャしたコンテンツと一致し、キャプチャがレターボックス表示にならないようにします。
リソースの復元
メディア プロジェクション セッションが停止して無効になったときに通知を受け取るように、アプリで MediaProjection onStop() コールバックを登録する必要があります。セッションが停止したら、アプリは仮想ディスプレイやプロジェクション サーフェスなどの保持しているリソースを解放する必要があります。停止したメディア プロジェクション セッションでは、アプリがそのメディア プロジェクションの仮想ディスプレイを以前に作成していない場合でも、新しい仮想ディスプレイを作成できなくなります。
メディア プロジェクションが終了すると、システムはコールバックを呼び出します。この終了には、次のような理由が考えられます。
- ユーザーがアプリの UI またはシステムの メディア プロジェクション ステータスバー チップを使用してセッションを停止する
- 画面がロックされている
- 別のメディア プロジェクション セッションが開始される
- アプリプロセスが強制終了される
アプリがコールバックを登録していない場合、createVirtualDisplay()
は IllegalStateExceptionをスローします。
オプトアウト
Android 14 以降では、アプリの画面共有がデフォルトで有効になっています。メディア プロジェクション セッションごとに、アプリ ウィンドウまたはディスプレイ全体を共有するオプションが用意されています。
アプリは、
createScreenCaptureIntent(MediaProjectionConfig) メソッドを
MediaProjectionConfig 引数を使用して
createConfigForDefaultDisplay() の呼び出しから返すことで、アプリの画面共有をオプトアウトできます。
createScreenCaptureIntent(MediaProjectionConfig) の呼び出しから返された
MediaProjectionConfig 引数を使用して
createConfigForUserChoice() を呼び出すことは、デフォルトの動作(
createScreenCaptureIntent() の呼び出し)と同じです。
サイズ変更可能なアプリ
メディア プロジェクション アプリは、必ずサイズ変更可能
(resizeableActivity="true")にしてください。サイズ変更が可能なアプリは、デバイス設定の変更とマルチウィンドウ モードをサポートします(マルチウィンドウのサポートをご覧ください)。
アプリがサイズ変更不可の場合は、ウィンドウ
コンテキストから表示境界をクエリし、getMaximumWindowMetrics() を使用して、WindowMetrics の
アプリで利用可能な最大表示領域を取得する必要があります。
Kotlin
val windowContext = context.createWindowContext(context.display!!, WindowManager.LayoutParams.TYPE_APPLICATION, null) val projectionMetrics = windowContext.getSystemService(WindowManager::class.java) .maximumWindowMetrics
Java
Context windowContext = context.createWindowContext(context.getDisplay(), WindowManager.LayoutParams.TYPE_APPLICATION, null); WindowMetrics projectionMetrics = windowContext.getSystemService(WindowManager.class) .getMaximumWindowMetrics();
ステータスバー チップと自動停止
画面投影の悪用は、デバイスの画面が共有されていることにユーザーが気付かないため、金融情報などの機密データが漏洩する可能性があります。
Android 15 QPR1 以降を搭載したデバイスで実行されているアプリの場合、進行中の画面投影をユーザーに知らせるために、ステータスバーに大きく目立つチップが表示されます。ユーザーはチップをタップして、画面の共有、キャスト、録画を停止できます。また、デバイスの画面がロックされると、画面投影は自動的に停止します。
画面共有、キャスト、録画を開始して、メディア プロジェクション ステータスバー チップの可用性をテストします。チップはステータスバーに表示されます。
ステータスバー チップのユーザー操作やロック画面の有効化によって画面投影が停止したときに、アプリがリソースを解放して UI を更新するようにするには、次の操作を行います。
MediaProjection.Callbackのインスタンスを作成します。コールバック
onStop()メソッドを実装します。画面投影が停止すると、このメソッドが呼び出されます。アプリが保持しているリソースを解放し、必要に応じてアプリの UI を更新します。
コールバックをテストするには、ステータスバー チップをタップするか、デバイス画面をロックして画面投影を停止します。onStop() メソッドが呼び出され、アプリが意図したとおりに応答することを確認します。
参考情報
メディア プロジェクションの詳細については、動画と音声の 再生のキャプチャをご覧ください。