【问题标题】:How to take a Screenshot from a background-service class using MediaProjection API?如何使用 MediaProjection API 从后台服务类中截取屏幕截图?
【发布时间】:2020-11-06 08:58:49
【问题描述】:

在对该主题进行了大量研究后,虽然我找到了一些答案,但我无法理解 MediaProjection API 的工作原理。

我想从后台服务类中截取设备的屏幕截图。有没有可能做到。我有一个 MainActivity.java 启动一个 serviceIntent 到另一个类,它是一个服务(不是一个活动)。所以我想在这个服务类中实现这个API。请帮帮我

【问题讨论】:

    标签: java android android-service screenshot android-mediaprojection


    【解决方案1】:

    这是实现这一目标的棘手方法。

    首先你需要创建透明的背景主题。

        <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>
    

    现在您需要在 Manifest 文件中的 ScreenShotActivity 上添加此应用此主题。

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

    你的 ScreenShotActivity 类。

    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;
            }
        }
    }
    

    在您的清单文件中添加这些权限。

       <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" />
    

    现在魔术从这里开始,您需要像这样从您的服务中调用 ScreenShotActivity。

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

    【讨论】:

    • 哦,好吧。这是一种非常聪明的做法。谢谢
    • 我应该如何导入 PostWebAPIData 以及它有什么作用?
    • @Saatwik 只需从您的代码中删除 PostWebAPIData 并运行
    • 好的,但是 postScreenShot 方法呢?
    • @Saatwik 你想在服务器上的某个地方发布这个截图,比如使用 Larave; API 的..?
    猜你喜欢
    • 1970-01-01
    • 2015-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多