おはよう君需要なし

求不得苦な日々

Androidアプリからスクリーンショットを撮る【情報求む】

はじめに

先日、Pokemon Goの個体値判定アプリを製作中~という話を書かせていただきましたが、友人から「私の端末で動かない」というご報告をいただきました。

nodemand.hatenablog.com

正直、他の端末でのテストをまったく行っていなかったというのもあり、その報告は想定の範囲内でしたが、動かない端末はかなり多いみたいです。

そもそもどうやって動作させるか

このアプリを作るとき、私は手動でユーザーにスクリーンショットを撮ってもらう、という方式を取っていました。この方法であればユーザーにとって処理がトリガーされるタイミングが明確であり、PokemonGOのアプリの上にボタンをオーバーレイさせるなどの必要がなくなるなどメリットが大きいと思ったためです。

Android 6.0ではFileObserverが動かん

ユーザーにスクリーンショットを撮らせ、それをアプリで感知させるにはFileObserverというものを使用すればよいのですが…

Issue 189231 - android - FileObserver is not running on Android 6.0 / Mushroom - Android Open Source Project - Issue Tracker - Google Project Hosting

どうもAndroid 6.0ではそれが使えないらしく、新しいスクリーンショットが来てもイベントが発生しないのだとか。

ボタンを押してもらってスクリーンショットを撮る方法

こういった問題があったことから、新しい方法をとることにしました。それが、オーバーレイでボタンをPokemonGOアプリの上に表示して、それを押したタイミングで現在スクリーンに表示されているモノを取得してしまおう、という方法です。

現在のスクリーンの内容を取得する

これを実現するには、VirtualDisplayというものを使用し、そこから出力される内容をImageReaderで取得する、という方法を取るのがセオリーみたいです。

techbooster.org

わからないこと

この記事を読んでもわからなかったこと(できなかったこと)を書きます。

ImageReader.newInstanceの指定フォーマット

mImageReader = ImageReader
        .newInstance(mWidth, mHeight, ImageFormat.RGB_565, 2);

このtechboosterの記事中では、newInstanceのフォーマット指定にRGB_565を指定していますが、私の端末(Galaxy j)では不適切なようで、

 UnsupportedOperationException: The producer output buffer format 0x1 doesn't match the ImageReader's configured buffer format 0x4.

といった例外が発生してしまいました。例外の意味としては、「the producer(ここではVirtualDisplay?)がPixelFormat.RGBA_8888(0x1)で出力しているのにImageReaderはPixelFormat.RGB_565(0x4)と設定されてるよ」って話だと思います。実際に、newInstanceでRGBA_8888を指定すると例外なく動くようになりました。

これってどうすればいいの?

というわけで、今回は対症療法的に直すことができましたが、どうもしっくりきません。

というのも、全てのデバイスでPixelFormat.RGBA_8888で出力されているわけではないようなので(実際にtechboosterの記事ではRGB_565で動いているわけで)、他の端末でやってみたら動きませんでしたでは話にならないためです。

どうにかして、事前にVirtualDisplayの出力するフォーマットを知ることはできないのでしょうか?

他の問題?

手元にほかにテストできる端末がなかったので、エミュレータでこのプログラムを試してみたのですが、真っ黒な画面が返されるだけでうまくキャプチャすることができませんでした。

これについては、以下のissueにあるように、エミュレータがサポートしていないのが問題のようです。

github.com

おわりに

せっかく作ったアプリなので、このまま終わらせたくはないと思っています。いずれにせよVirtualDisplayから画面情報を得るにはAndroid5.0以上が要求されるためAndroid4.4を切り捨てなければならないことになるためです。

Android6.0においてスクリーンショットを検知できない問題は、ボタンでトリガーするようにすればいいかなとも考えています(それだと2ステップ必要になるので良くないが。)

というわけで、情報求む!って感じです(VirtualDisplayもFileObserverも。)