Closed AndroidDeveloperLB closed 3 years ago
Hmm, interesting one.
Why don't you add a boolean flag like shouldCapture
and then, after the first successful image stop projection and set this to false so that no additional images will be captured. Something like
private boolean shouldCapture;
if (image != null && shouldCapture) {
// do the capturing
// call a stop method
stopProjection();
}
private void startProjection() {
shouldCapture = true;
startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);
}
private void stopProjection() {
mHandler.post(new Runnable() {
@Override
public void run() {
shouldCapture = false;
if (sMediaProjection != null) {
sMediaProjection.stop();
}
}
});
}
I think it's all because of the multiple threads that work here. Here's what I did. I made a manager class for the screenshots, which by a single call to "takeScreenshot" (after the app was given the permission to record screen, using "requestScreenshotPermission" and then "onActivityResult") :
public class ScreenshotManager {
private static final String SCREENCAP_NAME = "screencap";
private static final int VIRTUAL_DISPLAY_FLAGS = DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
public static final ScreenshotManager INSTANCE = new ScreenshotManager();
private Intent mIntent;
private ScreenshotManager() {
}
public void requestScreenshotPermission(@NonNull Activity activity, int requestId) {
MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
activity.startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), requestId);
}
public void onActivityResult(int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && data != null)
mIntent = data;
else mIntent = null;
}
@UiThread
public boolean takeScreenshot(@NonNull Context context) {
if (mIntent == null)
return false;
final MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) context.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
final MediaProjection mediaProjection = mediaProjectionManager.getMediaProjection(Activity.RESULT_OK, mIntent);
if (mediaProjection == null)
return false;
final int density = context.getResources().getDisplayMetrics().densityDpi;
final Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
final Point size = new Point();
display.getSize(size);
final int width = size.x, height = size.y;
final ImageReader imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 1);
final VirtualDisplay virtualDisplay = mediaProjection.createVirtualDisplay(SCREENCAP_NAME, width, height, density, VIRTUAL_DISPLAY_FLAGS, imageReader.getSurface(), null, null);
imageReader.setOnImageAvailableListener(new OnImageAvailableListener() {
@Override
public void onImageAvailable(final ImageReader reader) {
Log.d("AppLog", "onImageAvailable");
mediaProjection.stop();
new AsyncTask<Void, Void, Bitmap>() {
@Override
protected Bitmap doInBackground(final Void... params) {
Image image = null;
Bitmap bitmap = null;
try {
image = reader.acquireLatestImage();
if (image != null) {
Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride(), rowStride = planes[0].getRowStride(), rowPadding = rowStride - pixelStride * width;
bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
return bitmap;
}
} catch (Exception e) {
if (bitmap != null)
bitmap.recycle();
e.printStackTrace();
}
if (image != null)
image.close();
reader.close();
return null;
}
@Override
protected void onPostExecute(final Bitmap bitmap) {
super.onPostExecute(bitmap);
Log.d("AppLog", "Got bitmap?" + (bitmap != null));
}
}.execute();
}
}, null);
mediaProjection.registerCallback(new Callback() {
@Override
public void onStop() {
super.onStop();
if (virtualDisplay != null)
virtualDisplay.release();
imageReader.setOnImageAvailableListener(null, null);
mediaProjection.unregisterCallback(this);
}
}, null);
return true;
}
}
Should work. You definitely need some other reader than the one provided in the demo. Can't help but wonder what would happen if first image grabbed is null though, this is why I was thinking of stopping projection after at least one image is captured. Edge case though, probably never happen in real life.
I have a few questions:
Just wanted to ask what would need to be changed, to get a single screenshot and be done with it till next time it will need to take a screenshot (if at all), avoiding the notification when not needed.
I tried to call "stopProjection" right in the "onImageAvailable" function, but it still got more than one image.