Last active
November 3, 2021 04:48
-
-
Save mtsahakis/a4dca46b80cb8ac2cd100a7a52f65b1d to your computer and use it in GitHub Desktop.
Demo Service for https://github.com/mtsahakis/MediaProjectionDemo/issues/7
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.mtsahakis.mediaprojectiondemo; | |
import android.app.Activity; | |
import android.app.Service; | |
import android.content.Context; | |
import android.content.Intent; | |
import android.graphics.Bitmap; | |
import android.graphics.PixelFormat; | |
import android.graphics.Point; | |
import android.hardware.display.DisplayManager; | |
import android.hardware.display.VirtualDisplay; | |
import android.media.Image; | |
import android.media.ImageReader; | |
import android.media.projection.MediaProjection; | |
import android.media.projection.MediaProjectionManager; | |
import android.os.Handler; | |
import android.os.IBinder; | |
import android.os.Looper; | |
import android.util.DisplayMetrics; | |
import android.util.Log; | |
import android.view.Display; | |
import android.view.OrientationEventListener; | |
import android.view.WindowManager; | |
import java.io.File; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.nio.ByteBuffer; | |
public class ScreenCaptureService extends Service { | |
private static final String TAG = "ScreenCaptureService"; | |
private static final String RESULT_CODE = "RESULT_CODE"; | |
private static final String DATA = "DATA"; | |
private static final String ACTION = "ACTION"; | |
private static final String START = "START"; | |
private static final String STOP = "STOP"; | |
private static final String SCREENCAP_NAME = "screencap"; | |
private static int IMAGES_PRODUCED; | |
private MediaProjection mMediaProjection; | |
private String mStoreDir; | |
private ImageReader mImageReader; | |
private Handler mHandler; | |
private Display mDisplay; | |
private VirtualDisplay mVirtualDisplay; | |
private int mDensity; | |
private int mWidth; | |
private int mHeight; | |
private int mRotation; | |
private OrientationChangeCallback mOrientationChangeCallback; | |
public static Intent getStartIntent(Context context, int resultCode, Intent data) { | |
Intent intent = new Intent(context, ScreenCaptureService.class); | |
intent.putExtra(ACTION, START); | |
intent.putExtra(RESULT_CODE, resultCode); | |
intent.putExtra(DATA, data); | |
return intent; | |
} | |
public static Intent getStopIntent(Context context) { | |
Intent intent = new Intent(context, ScreenCaptureService.class); | |
intent.putExtra(ACTION, STOP); | |
return intent; | |
} | |
private static boolean isValidStartIntent(Intent intent) { | |
return intent.hasExtra(RESULT_CODE) && intent.hasExtra(DATA) | |
&& intent.hasExtra(ACTION) && intent.getStringExtra(ACTION).equals(START); | |
} | |
private static boolean isValidStopIntent(Intent intent) { | |
return intent.hasExtra(ACTION) && intent.getStringExtra(ACTION).equals(STOP); | |
} | |
private static int getVirtualDisplayFlags() { | |
return DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; | |
} | |
private class ImageAvailableListener implements ImageReader.OnImageAvailableListener { | |
@Override | |
public void onImageAvailable(ImageReader reader) { | |
Image image = null; | |
FileOutputStream fos = null; | |
Bitmap bitmap = null; | |
try { | |
image = mImageReader.acquireLatestImage(); | |
if (image != null) { | |
Image.Plane[] planes = image.getPlanes(); | |
ByteBuffer buffer = planes[0].getBuffer(); | |
int pixelStride = planes[0].getPixelStride(); | |
int rowStride = planes[0].getRowStride(); | |
int rowPadding = rowStride - pixelStride * mWidth; | |
// create bitmap | |
bitmap = Bitmap.createBitmap(mWidth + rowPadding / pixelStride, mHeight, Bitmap.Config.ARGB_8888); | |
bitmap.copyPixelsFromBuffer(buffer); | |
// write bitmap to a file | |
fos = new FileOutputStream(mStoreDir + "/myscreen_" + IMAGES_PRODUCED + ".png"); | |
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); | |
IMAGES_PRODUCED++; | |
Log.e(TAG, "captured image: " + IMAGES_PRODUCED); | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} finally { | |
if (fos != null) { | |
try { | |
fos.close(); | |
} catch (IOException ioe) { | |
ioe.printStackTrace(); | |
} | |
} | |
if (bitmap != null) { | |
bitmap.recycle(); | |
} | |
if (image != null) { | |
image.close(); | |
} | |
} | |
} | |
} | |
private class OrientationChangeCallback extends OrientationEventListener { | |
OrientationChangeCallback(Context context) { | |
super(context); | |
} | |
@Override | |
public void onOrientationChanged(int orientation) { | |
final int rotation = mDisplay.getRotation(); | |
if (rotation != mRotation) { | |
mRotation = rotation; | |
try { | |
// clean up | |
if (mVirtualDisplay != null) mVirtualDisplay.release(); | |
if (mImageReader != null) mImageReader.setOnImageAvailableListener(null, null); | |
// re-create virtual display depending on device width / height | |
createVirtualDisplay(); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
} | |
private class MediaProjectionStopCallback extends MediaProjection.Callback { | |
@Override | |
public void onStop() { | |
Log.e(TAG, "stopping projection."); | |
mHandler.post(new Runnable() { | |
@Override | |
public void run() { | |
if (mVirtualDisplay != null) mVirtualDisplay.release(); | |
if (mImageReader != null) mImageReader.setOnImageAvailableListener(null, null); | |
if (mOrientationChangeCallback != null) mOrientationChangeCallback.disable(); | |
mMediaProjection.unregisterCallback(MediaProjectionStopCallback.this); | |
} | |
}); | |
} | |
} | |
@Override | |
public IBinder onBind(Intent intent) { | |
return null; | |
} | |
@Override | |
public void onCreate() { | |
super.onCreate(); | |
// create store dir | |
File externalFilesDir = getExternalFilesDir(null); | |
if (externalFilesDir != null) { | |
mStoreDir = externalFilesDir.getAbsolutePath() + "/screenshots/"; | |
File storeDirectory = new File(mStoreDir); | |
if (!storeDirectory.exists()) { | |
boolean success = storeDirectory.mkdirs(); | |
if (!success) { | |
Log.e(TAG, "failed to create file storage directory."); | |
stopSelf(); | |
} | |
} | |
} else { | |
Log.e(TAG, "failed to create file storage directory, getExternalFilesDir is null."); | |
stopSelf(); | |
} | |
// start capture handling thread | |
new Thread() { | |
@Override | |
public void run() { | |
Looper.prepare(); | |
mHandler = new Handler(); | |
Looper.loop(); | |
} | |
}.start(); | |
} | |
@Override | |
public int onStartCommand(Intent intent, int flags, int startId) { | |
if (isValidStartIntent(intent)) { | |
int resultCode = intent.getIntExtra(RESULT_CODE, Activity.RESULT_CANCELED); | |
Intent data = intent.getParcelableExtra(DATA); | |
startProjection(resultCode, data); | |
} | |
if (isValidStopIntent(intent)) { | |
stopProjection(); | |
stopSelf(); | |
} | |
return START_NOT_STICKY; | |
} | |
private void startProjection(int resultCode, Intent data) { | |
MediaProjectionManager mpManager = | |
(MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE); | |
if (mMediaProjection == null) { | |
mMediaProjection = mpManager.getMediaProjection(resultCode, data); | |
if (mMediaProjection != null) { | |
// display metrics | |
DisplayMetrics metrics = getResources().getDisplayMetrics(); | |
mDensity = metrics.densityDpi; | |
WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); | |
mDisplay = windowManager.getDefaultDisplay(); | |
// create virtual display depending on device width / height | |
createVirtualDisplay(); | |
// register orientation change callback | |
mOrientationChangeCallback = new OrientationChangeCallback(this); | |
if (mOrientationChangeCallback.canDetectOrientation()) { | |
mOrientationChangeCallback.enable(); | |
} | |
// register media projection stop callback | |
mMediaProjection.registerCallback(new MediaProjectionStopCallback(), mHandler); | |
} | |
} | |
} | |
private void stopProjection() { | |
if (mHandler != null) { | |
mHandler.post(new Runnable() { | |
@Override | |
public void run() { | |
if (mMediaProjection != null) { | |
mMediaProjection.stop(); | |
} | |
} | |
}); | |
} | |
} | |
private void createVirtualDisplay() { | |
// get width and height | |
Point size = new Point(); | |
mDisplay.getSize(size); | |
mWidth = size.x; | |
mHeight = size.y; | |
// start capture reader | |
mImageReader = ImageReader.newInstance(mWidth, mHeight, PixelFormat.RGBA_8888, 2); | |
mVirtualDisplay = mMediaProjection.createVirtualDisplay(SCREENCAP_NAME, mWidth, mHeight, | |
mDensity, getVirtualDisplayFlags(), mImageReader.getSurface(), null, mHandler); | |
mImageReader.setOnImageAvailableListener(new ImageAvailableListener(), mHandler); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment