These are the errors I am getting:
2021-10-31 12:18:44.281 15783-15921/com.exabes.test E/AndroidRuntime: FATAL EXCEPTION: Thread-7
Process: com.exabes.test, PID: 15783
java.lang.IllegalStateException: Cannot start already started MediaProjection
at android.os.Parcel.createException(Parcel.java:1974)
at android.os.Parcel.readException(Parcel.java:1934)
at android.os.Parcel.readException(Parcel.java:1884)
at android.media.projection.IMediaProjection$Stub$Proxy.start(IMediaProjection.java:144)
at android.media.projection.MediaProjection.<init>(MediaProjection.java:69)
at android.media.projection.MediaProjectionManager.getMediaProjection(MediaProjectionManager.java:169)
at com.autobot.service.AbstractNotificationService.startCapture(AbstractNotificationService.java:241)
at com.exabes.test.ExampleThread.run(ExampleThread.java:813)
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.media.projection.MediaProjectionManagerService$MediaProjection.start(MediaProjectionManagerService.java:416)
at android.media.projection.IMediaProjection$Stub.onTransact(IMediaProjection.java:52)
at android.os.Binder.execTransact(Binder.java:739)
and:
2021-10-31 12:35:40.293 17831-17992/com.exabes.test E/AndroidRuntime: FATAL EXCEPTION: Thread-7
Process: com.exabes.test, PID: 17831
java.lang.NullPointerException: Attempt to invoke virtual method 'android.hardware.display.VirtualDisplay android.media.projection.MediaProjection.createVirtualDisplay(java.lang.String, int, int, int, int, android.view.Surface, android.hardware.display.VirtualDisplay$Callback, android.os.Handler)' on a null object reference
at com.exabes.test.AbstractNotificationService.startCapture(AbstractNotificationService.java:251)
at com.exabes.test.ExampleThread.run(ExampleThread.java:813)
I am basically requesting the createScreenCaptureIntent
in the MainActivity and pass the results to the AccessebilityService where all the methods necessary to take a screenshots are declared. Then I simply call startCapture()
into the Thread.
I am gonna post most of the code.
MainActivity:
public class MainActivity extends Activity {
private static final int DRAW_OVER_OTHER_APP_PERMISSION = 123;
private static final int REQUEST_SCREENSHOT=59706;
private MediaProjectionManager mgr;
int resultCodeX;
Intent dataX;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); //TODO
mgr=(MediaProjectionManager)getSystemService(MEDIA_PROJECTION_SERVICE);
startActivityForResult(mgr.createScreenCaptureIntent(), REQUEST_SCREENSHOT);
this.askPermissions();
...
}
public void startService()
{
// To prevent starting the service if the required permission is NOT granted.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(MainActivity.this)) {
Intent intent = new Intent(MainActivity.this, ExampleService.class);
intent.putExtra(getString(R.string.edit_text_name), Objects.requireNonNull(trainerUsernameEditText.getText()).toString());
intent.putExtra(ExampleService.EXTRA_RESULT_CODE, resultCodeX);
intent.putExtra(ExampleService.EXTRA_RESULT_INTENT, dataX);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
startForegroundService(intent);
else
startService(intent);
finish();
} else {
error();
askForSystemOverlayPermission();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SCREENSHOT) {
if (resultCode == RESULT_OK) {
//TODO
resultCodeX=resultCode;
dataX=data;
//finish();
}
}
else if (requestCode == DRAW_OVER_OTHER_APP_PERMISSION) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
//Permission is not available. Display error text.
errorToast();
finish();
}
}
}
else {
super.onActivityResult(requestCode, resultCode, data);
}
}}
AccesibilityService:
public abstract class AbstractNotificationService extends AccessibilityService {
private static final String ACTION_STOP = "STOP";
private NotificationCompat.Builder builder;
private NotificationManagerCompat notificationManager;
private ActivityManager activityManager;
private static final String CHANNEL_WHATEVER="channel_whatever";
private static final int NOTIFY_ID=9906;
public static final String EXTRA_RESULT_CODE="resultCode";
public static final String EXTRA_RESULT_INTENT="resultIntent";
static final String ACTION_RECORD=
BuildConfig.APPLICATION_ID+".RECORD";
static final String ACTION_SHUTDOWN=
BuildConfig.APPLICATION_ID+".SHUTDOWN";
static final int VIRT_DISPLAY_FLAGS=
DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY |
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
private MediaProjection projection;
private VirtualDisplay vdisplay;
final private HandlerThread handlerThread=
new HandlerThread(getClass().getSimpleName(),
android.os.Process.THREAD_PRIORITY_BACKGROUND);
private Handler handler;
private MediaProjectionManager mgr;
private WindowManager wmgr;
private ImageTransmogrifier it;
private int resultCode;
private Intent resultData;
final private ToneGenerator beeper=
new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100);
@Override
public void onCreate() {
super.onCreate();
activityManager = (ActivityManager) getSystemService(Activity.ACTIVITY_SERVICE);
mgr=(MediaProjectionManager)getSystemService(MEDIA_PROJECTION_SERVICE);
wmgr=(WindowManager)getSystemService(WINDOW_SERVICE);
handlerThread.start();
handler=new Handler(handlerThread.getLooper());
}
@Override
public int onStartCommand (Intent intent, int flags, int startId) {
String action = intent.getAction();
if (action != null) {
if (action.equals(getString(R.string.stop_foreground_action))) {
stopForeground(true);
stopSelf();
return START_NOT_STICKY;
}
}
else {
resultCode=intent.getIntExtra(EXTRA_RESULT_CODE, 1337);
resultData=intent.getParcelableExtra(EXTRA_RESULT_INTENT);
//foregroundify();
}
//return START_STICKY;
return START_REDELIVER_INTENT;
}
@Override
public void onDestroy() {
super.onDestroy();
//TODO
beeper.startTone(ToneGenerator.TONE_PROP_NACK);
stopCapture();
}
public abstract void sendNotification(String response);
public void killBackground(String packageName)
{
activityManager.killBackgroundProcesses(packageName);
}
public WindowManager getWindowManager() {
return(wmgr);
}
public Handler getHandler() {
return(handler);
}
public void processImage(final byte[] png) {
new Thread() {
@Override
public void run() {
//File output=new File(getExternalFilesDir(null),"screenshot.png");
File output=new File(Environment.getExternalStorageDirectory(),"screenshot.png");
try {
FileOutputStream fos=new FileOutputStream(output);
fos.write(png);
fos.flush();
//fos.getFD().sync();
fos.close();
MediaScannerConnection.scanFile(AbstractNotificationService.this,
new String[] {output.getAbsolutePath()},
new String[] {"image/png"},
null);
}
catch (Exception e) {
Log.e(getClass().getSimpleName(), "Exception writing out screenshot", e);
}
}
}.start();
beeper.startTone(ToneGenerator.TONE_PROP_ACK);
stopCapture();
}
private void stopCapture() {
if (projection!=null) {
projection.stop();
vdisplay.release();
projection=null;
}
}
public void startCapture() {
projection=mgr.getMediaProjection(resultCode, resultData);
it=new ImageTransmogrifier(this);
MediaProjection.Callback cb=new MediaProjection.Callback() {
@Override
public void onStop() {
vdisplay.release();
}
};
vdisplay=projection.createVirtualDisplay("andshooter",
it.getWidth(), it.getHeight(),
getResources().getDisplayMetrics().densityDpi,
VIRT_DISPLAY_FLAGS, it.getSurface(), null, handler);
projection.registerCallback(cb, handler);
}}
Thread:
public class ExampleThread extends Thread {
private AbstractNotificationService notificationService;
public ExampleThread(AbstractNotificationService notificationService)
{
this.notificationService = notificationService;
}
@Override
public void run()
{
while(boolean){
...
notificationService.startCapture();
}
notificationService.sendNotification("ENDED");
}}