Search code examples
javaandroidandroid-servicescreenshotandroid-mediaprojection

How to take a Screenshot from a background-service class using MediaProjection API?


After doing a lot of research on this topic, though I found a few answers, I could not understand how the MediaProjection API works.

I want to take a screenshot of the device from a background Service class. Is it possible to do it. I have one MainActivity.java which starts a serviceIntent to another class which is a service(not an activity). So I want to implement this API in this service class. Please help me


Solution

  • This is tricky way to achieve this.

    First of all you need to create transparent background theme like.

        <style name="transparentTheme" parent="Theme.AppCompat.NoActionBar">
            <item name="android:background">#00000000</item> <!-- Or any transparency or color you need -->
            <item name="android:windowNoTitle">true</item>
            <item name="android:windowBackground">@android:color/transparent</item>
            <item name="android:colorBackgroundCacheHint">@null</item>
            <item name="android:windowIsTranslucent">true</item>
            <item name="android:windowAnimationStyle">@android:style/Animation</item>
            <item name="android:navigationBarColor" tools:ignore="NewApi">#00000000</item>
            <item name="android:statusBarColor" tools:ignore="NewApi">#00000000</item>
        </style>
    

    Now you need to add this apply this theme on your ScreenShotActivity in Manifest file.

      <activity
                android:name=".Activities.ScreenShotActivity"
                android:theme="@style/transparentTheme" />
            <activity
    

    Your ScreenShotActivity Class.

    public class ScreenShotActivity extends Activity {
    
        private static final int videoTime = 5000;
        private static final int REQUEST_CODE = 1000;
        private static final int REQUEST_PERMISSION = 1000;
        private static final SparseIntArray ORIENTATION = new SparseIntArray();
        private MediaProjectionManager mediaProjectionManager;
        private MediaProjection mediaProjection;
        private VirtualDisplay virtualDisplay;
        private ScreenShotActivity.MediaProjectionCallback mediaProjectionCallback;
        private MediaRecorder mediaRecorder;
        PostWebAPIData postWebAPIData;
        private int mScreenDensity;
        private static int DISPLAY_WIDTH = 720;
        private static int DISPLAY_HEIGHT = 1280;
    
        static {
            ORIENTATION.append(Surface.ROTATION_0, 90);
            ORIENTATION.append(Surface.ROTATION_90, 0);
            ORIENTATION.append(Surface.ROTATION_180, 270);
            ORIENTATION.append(Surface.ROTATION_270, 180);
        }
    
        private String screenShotUri = "";
    
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_screen_shot);
            init();
        }
    
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        private void init() {
            //Screen tracking Code Started here..............................
            DisplayMetrics metrics = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(metrics);
            mScreenDensity = metrics.densityDpi;
            postWebAPIData = new PostWebAPIData();
            DISPLAY_HEIGHT = metrics.heightPixels;
            DISPLAY_WIDTH = metrics.widthPixels;
    
            mediaRecorder = new MediaRecorder();
            mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
            if (ContextCompat.checkSelfPermission(ScreenShotActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    + ContextCompat.checkSelfPermission(ScreenShotActivity.this, Manifest.permission.RECORD_AUDIO)
                    != PackageManager.PERMISSION_GRANTED) {
                if (ActivityCompat.shouldShowRequestPermissionRationale(ScreenShotActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) ||
                        ActivityCompat.shouldShowRequestPermissionRationale(ScreenShotActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                } else {
                    ActivityCompat.requestPermissions(ScreenShotActivity.this, new String[]{
                            Manifest.permission.WRITE_EXTERNAL_STORAGE,
                            Manifest.permission.RECORD_AUDIO
                    }, REQUEST_PERMISSION);
                }
            } else {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        toogleScreenShare();
                    }
                }, 500);
            }
        }
    
    
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        private void toogleScreenShare() {
            initRecorder();
            recordScreen();
        }
    
    
        public void getPathScreenShot(String filePath) {
            FFmpegMediaMetadataRetriever med = new FFmpegMediaMetadataRetriever();
    
            med.setDataSource(filePath);
            Bitmap bmp = med.getFrameAtTime(2 * 1000000, FFmpegMediaMetadataRetriever.OPTION_CLOSEST);
            String myPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + new StringBuilder("/screenshot").append(".bmp").toString();
    
            File myDir = new File(myPath);
            myDir.mkdirs();
            Random generator = new Random();
            int n = 10000;
            n = generator.nextInt(n);
            String fname = "Image-" + n + ".jpg";
            File file = new File(myDir, fname);
            Log.i(TAG, "" + myDir);
            if (myDir.exists())
                myDir.delete();
            try {
                FileOutputStream out = new FileOutputStream(myDir);
                bmp.compress(Bitmap.CompressFormat.JPEG, 90, out);
                out.flush();
                out.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            postScreenShot(myPath);
        }
    
       
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        private void recordScreen() {
            if (mediaProjection == null) {
                startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);
            } else {
                virtualDisplay = createVirtualDisplay();
                mediaRecorder.start();
                onBackPressed();
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mediaRecorder.stop();
                        mediaRecorder.reset();
                        stopRecordScreen();
                        destroyMediaProjection();
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                getPathScreenShot(screenShotUri);
                            }
                        }, 2000);
                    }
                }, videoTime);
            }
        }
    
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        private VirtualDisplay createVirtualDisplay() {
            return mediaProjection.createVirtualDisplay("MainActivity", DISPLAY_WIDTH, DISPLAY_HEIGHT, mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                    mediaRecorder.getSurface(), null, null);
        }
    
        private void initRecorder() {
            try {
                mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
                mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
                mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    
                screenShotUri = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + new StringBuilder("/screenshot").append(".mp4").toString();
    
                mediaRecorder.setOutputFile(screenShotUri);
                mediaRecorder.setVideoSize(DISPLAY_WIDTH, DISPLAY_HEIGHT);
                mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
                mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
                mediaRecorder.setVideoEncodingBitRate(512 * 1000);
                mediaRecorder.setVideoFrameRate(5);
    
                int rotation = getWindowManager().getDefaultDisplay().getRotation();
                int orientation = ORIENTATION.get(rotation + 90);
                mediaRecorder.setOrientationHint(orientation);
                mediaRecorder.prepare();
            } catch (IOException e) {
                e.printStackTrace();
                Log.d("ExceptionOccured", "" + e.getMessage());
            }
        }
    
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode != REQUEST_CODE) {
                stopService(new Intent(this, BackgroundService.class));
                startService(new Intent(this, BackgroundService.class));
                Toast.makeText(ScreenShotActivity.this, "Unknown Error", Toast.LENGTH_SHORT).show();
                Log.d("Livetracking", "ScreenShot" + requestCode + "  " + resultCode + " " + data);
                return;
            }
            if (resultCode != RESULT_OK) {
                stopService(new Intent(this, BackgroundService.class));
                startService(new Intent(this, BackgroundService.class));
                Toast.makeText(ScreenShotActivity.this, "Permission denied" + requestCode, Toast.LENGTH_SHORT).show();
                Log.d("Livetracking", "Screenshot" + requestCode + "  " + resultCode + " " + data);
                return;
            }
            Log.d("Livetracking", "Screenshot" + requestCode + "  " + resultCode + " " + data);
    
            mediaProjectionCallback = new ScreenShotActivity.MediaProjectionCallback();
            mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
            mediaProjection.registerCallback(mediaProjectionCallback, null);
            virtualDisplay = createVirtualDisplay();
            mediaRecorder.start();
            onBackPressed();
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    mediaRecorder.stop();
                    mediaRecorder.reset();
                    stopRecordScreen();
                    destroyMediaProjection();
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            getPathScreenShot(screenShotUri);
                        }
                    }, 2000);
                }
            }, videoTime);
            ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
            if (am != null) {
                List<ActivityManager.AppTask> tasks = am.getAppTasks();
                if (tasks != null && tasks.size() > 0) {
                    Log.d("RemovingApp", "recent");
                    tasks.get(0).setExcludeFromRecents(true);
                }
            }
        }
    
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        private class MediaProjectionCallback extends MediaProjection.Callback {
            @Override
            public void onStop() {
                mediaRecorder.stop();
                mediaRecorder.reset();
                mediaProjection = null;
                stopRecordScreen();
                destroyMediaProjection();
                if (mediaProjection != null) {
                    destroyMediaProjection();
                }
                super.onStop();
            }
        }
    
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        private void stopRecordScreen() {
            if (virtualDisplay == null) {
                virtualDisplay.release();
                if (mediaProjection != null) {
                    destroyMediaProjection();
                }
                return;
    
            }
        }
    
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        private void destroyMediaProjection() {
            if (mediaProjection != null) {
                mediaProjection.unregisterCallback(mediaProjectionCallback);
                mediaProjection.stop();
                mediaProjection = null;
            }
        }
    }
    

    add these permissions in your Manifest file.

       <uses-permission android:name="android.permission.RECORD_AUDIO" />
       <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
       <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    

    Now the magic start here you need to call your ScreenShotActivity from your service like this.

          Intent dialogIntent = new Intent(BackgroundService.this, ScreenShotActivity.class);
          dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          startActivity(dialogIntent);