如何使用 MediaProjection API 从后台服务 class 截取屏幕截图?
How to take a Screenshot from a background-service class using MediaProjection API?
在对这个主题做了很多研究之后,虽然我找到了一些答案,但我无法理解 MediaProjection API 是如何工作的。
我想从后台服务截取设备的屏幕截图class。有没有可能做到。我有一个 MainActivity.java 启动一个 serviceIntent 到另一个 class 这是一个服务(不是 activity)。所以我想在这个服务class中实现这个API。请帮助我
这是实现此目标的棘手方法。
首先你需要创建透明背景主题。
<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 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;
}
}
}
在您的清单文件中添加这些权限。
<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);
在对这个主题做了很多研究之后,虽然我找到了一些答案,但我无法理解 MediaProjection API 是如何工作的。
我想从后台服务截取设备的屏幕截图class。有没有可能做到。我有一个 MainActivity.java 启动一个 serviceIntent 到另一个 class 这是一个服务(不是 activity)。所以我想在这个服务class中实现这个API。请帮助我
这是实现此目标的棘手方法。
首先你需要创建透明背景主题。
<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 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;
}
}
}
在您的清单文件中添加这些权限。
<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);