31 private boolean mServiceStopped =
false;
32 private static final String QtTAG =
"QtScreenCaptureService";
33 private static final String CHANNEL_ID =
"ScreenCaptureChannel";
34 private static final String VIRTUAL_DISPLAY_NAME =
"ScreenCapture";
35 private VirtualDisplay mVirtualDisplay =
null;
36 private ImageReader mImageReader =
null;
37 private static int MAX_FRAMES_NUMBER = 12;
38 private MediaProjection mMediaProjection =
null;
39 private Handler mBackgroundHandler =
null;
40 private HandlerThread mBackgroundThread =
null;
42 private int mScreenWidth;
43 private int mScreenHeight;
45 static native
void onScreenFrameAvailable(
Image frame,
long id);
46 static native
void onErrorUpdate(
String errorString,
long id);
52 NotificationChannel
channel =
new NotificationChannel(CHANNEL_ID,
"Screen Capture",
53 NotificationManager.IMPORTANCE_LOW);
54 NotificationManager
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
58 Notification notification =
new Notification.Builder(
this, CHANNEL_ID)
59 .setSmallIcon(
android.R.drawable.ic_notification_overlay)
63 startForeground(1, notification);
68 synchronized (mServiceStopLock) {
71 return onStartCommandInternal(intent,
flags, startId);
75 private int onStartCommandInternal(Intent intent,
int flags,
int startId) {
82 int resultCode = intent.getIntExtra(QtScreenGrabber.RESULT_CODE, Activity.RESULT_CANCELED);
83 if (resultCode != Activity.RESULT_OK)
86 Intent
data = Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU ?
87 intent.getParcelableExtra(QtScreenGrabber.DATA) :
88 intent.getParcelableExtra(QtScreenGrabber.DATA, Intent.class);
89 mId = intent.getLongExtra(QtScreenGrabber.ID, -1);
90 if (
data ==
null || mId == -1) {
91 onErrorUpdate(
"Cannot parse Intent. Screen capture not started", mId);
95 mScreenWidth = intent.getIntExtra(QtScreenGrabber.WIDTH, 0);
96 mScreenHeight = intent.getIntExtra(QtScreenGrabber.HEIGHT, 0);
97 if (mScreenWidth <= 0 || mScreenHeight <= 0) {
98 onErrorUpdate(
"Wrong Screen size. Screen capture not started", mId);
103 MediaProjectionManager mgr = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
104 mMediaProjection = mgr.getMediaProjection(resultCode,
data);
105 mMediaProjection.registerCallback(
new MediaProjection.Callback() {
107 public void onStop() {
112 startScreenCapture();
113 }
catch (IllegalStateException | SecurityException e) {
114 onErrorUpdate(
"Cannot start MediaProjection: " + e.toString(), mId);
120 ImageReader.OnImageAvailableListener mScreenFrameListener =
new ImageReader.OnImageAvailableListener() {
122 public void onImageAvailable(ImageReader reader) {
128 Log.w(QtTAG,
"Null frame acquired. Skip it");
129 }
catch (Exception e) {
130 Log.w(QtTAG,
"The frame cannot be acquired: " + e);
135 private void startScreenCapture()
137 if (mMediaProjection ==
null)
140 mBackgroundThread =
new HandlerThread(
"ScreenCaptureThread");
141 mBackgroundThread.start();
142 mBackgroundHandler =
new Handler(mBackgroundThread.getLooper());
143 mImageReader = ImageReader.newInstance(mScreenWidth, mScreenHeight,
PixelFormat.RGBA_8888, MAX_FRAMES_NUMBER);
145 mVirtualDisplay = mMediaProjection.createVirtualDisplay(VIRTUAL_DISPLAY_NAME,
146 mScreenWidth, mScreenHeight, DisplayMetrics.DENSITY_MEDIUM,
147 DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, mImageReader.getSurface(),
148 null, mBackgroundHandler);
149 mImageReader.setOnImageAvailableListener(mScreenFrameListener, mBackgroundHandler);
152 void stopScreenCapture()
154 synchronized (mServiceStopLock) {
157 mServiceStopped =
true;
159 if (mImageReader !=
null)
160 mImageReader.setOnImageAvailableListener(
null, mBackgroundHandler);
162 if (mVirtualDisplay !=
null) {
163 mVirtualDisplay.release();
164 mVirtualDisplay =
null;
166 if (mBackgroundHandler !=
null) {
167 mBackgroundHandler.getLooper().quitSafely();
168 mBackgroundHandler =
null;
171 if (mBackgroundThread !=
null) {
172 mBackgroundThread.quitSafely();
174 mBackgroundThread.join();
175 }
catch (InterruptedException e) {
176 Log.w(QtTAG,
"The thread is interrupted. Cannot join: " + e);
178 mBackgroundThread =
null;
183 private final IBinder binder =
new ScreenCaptureBinder();
185 class ScreenCaptureBinder
extends Binder {
186 QtScreenCaptureService getService() {
187 return QtScreenCaptureService.this;
201 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
202 stopForeground(
true);
204 stopForeground(STOP_FOREGROUND_REMOVE);
206 if (mMediaProjection !=
null) {
207 mMediaProjection.stop();
208 mMediaProjection =
null;