Android: 以编程方式截取通知栏的屏幕截图
Android: Take screenshot of Notification bar Programmatically
我使用无障碍服务来扩展接收通知的通知栏。我正在尝试在通知抽屉中截取通知的屏幕截图。
从辅助功能服务的文档中,可以从 Android P 截取设备的屏幕截图。
由于我的应用程序不在前台,是否还有其他可能截取通知抽屉的屏幕截图。后台是运行
是的,你可以做到这一点,尽管这很棘手。诀窍是结合使用媒体投影管理器和与您的服务位于同一包中的 Activity。然后,您可以利用 MediaProjectionManager 捕获图像的能力以及共享存储来抓取屏幕截图。
在创建 AccessibilityService 时执行此操作:
@Override
public void onCreate() {
//Launch image capture intent for Color Contrast.
final Intent imageCaptureIntent = new Intent(this, ImageCaptureActivity.class);
imageCaptureIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
imageCaptureIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(imageCaptureIntent);
}
那么您的 ImageCaptureActivity 将只是一个标准 activity,但不会有任何 UI。它只会管理与媒体投影管理器的交互。在我的例子中,它最终是一个像素清晰的点。这实际上很难设置。我将复制我的 ImageCaptureActivity。这可能不会完全适合你,但当我深入研究这个问题时,我发现这个过程的记录非常糟糕。我没有对此进行篡改,但也许它会对你有所帮助。
public class ImageCaptureActivity extends AppCompatActivity {
private static final int REQUEST_MEDIA_PROJECTION = 1;
private MediaProjectionManager mProjectionManager;
private String mFileName;
private MediaProjection mMediaProjection = null;
private VirtualDisplay mVirtualDisplay;
private ImageReader mImageReader;
private static final int MAX_IMAGE_BUFFER = 10;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_capture);
mFileName = getFilesDir() + RuleColorContrast.IMAGE_CAPTURE_FILE_NAME;
OrientationChangedListener mOrientationChangedListener = new OrientationChangedListener(this);
mOrientationChangedListener.enable();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mProjectionManager = (MediaProjectionManager)getSystemService(MEDIA_PROJECTION_SERVICE);
startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (requestCode == REQUEST_MEDIA_PROJECTION) {
String message;
if (resultCode != Activity.RESULT_OK) {
message = "Media Projection Declined";
mMediaProjection = null;
} else {
message = "Media Projection Accepted";
mMediaProjection = mProjectionManager.getMediaProjection(resultCode, resultData);
attachImageCaptureOverlay();
}
Toast toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
toast.show();
finish();
}
}
private class OrientationChangedListener extends OrientationEventListener {
int mLastOrientation = -1;
OrientationChangedListener(Context context) {
super(context);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onOrientationChanged(int orientation) {
final int screenOrientation = getWindowManager().getDefaultDisplay().getRotation();
if (mVirtualDisplay == null) return;
if (mLastOrientation == screenOrientation) return;
mLastOrientation = screenOrientation;
detachImageCaptureOverlay();
attachImageCaptureOverlay();
}
}
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image == null || image.getPlanes().length <= 0) return;
final Image.Plane plane = image.getPlanes()[0];
final int rowPadding = plane.getRowStride() - plane.getPixelStride() * image.getWidth();
final int bitmapWidth = image.getWidth() + rowPadding / plane.getPixelStride();
final Bitmap tempBitmap = Bitmap.createBitmap(bitmapWidth, image.getHeight(), Bitmap.Config.ARGB_8888);
tempBitmap.copyPixelsFromBuffer(plane.getBuffer());
Rect cropRect = image.getCropRect();
final Bitmap bitmap = Bitmap.createBitmap(tempBitmap, cropRect.left, cropRect.top, cropRect.width(), cropRect.height());
//Do something with the bitmap
image.close();
}
};
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private void attachImageCaptureOverlay() {
if (mMediaProjection == null) return;
final DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
mImageReader = ImageReader.newInstance(metrics.widthPixels, metrics.heightPixels, PixelFormat.RGBA_8888, MAX_IMAGE_BUFFER);
mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCaptureTest",
metrics.widthPixels, metrics.heightPixels, metrics.densityDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(), null, null);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, null);
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void detachImageCaptureOverlay() {
mVirtualDisplay.release();
mImageReader.close();
}
}
注意:这种方法早在 Android 5.0.
就可以使用
我使用无障碍服务来扩展接收通知的通知栏。我正在尝试在通知抽屉中截取通知的屏幕截图。
从辅助功能服务的文档中,可以从 Android P 截取设备的屏幕截图。
由于我的应用程序不在前台,是否还有其他可能截取通知抽屉的屏幕截图。后台是运行
是的,你可以做到这一点,尽管这很棘手。诀窍是结合使用媒体投影管理器和与您的服务位于同一包中的 Activity。然后,您可以利用 MediaProjectionManager 捕获图像的能力以及共享存储来抓取屏幕截图。
在创建 AccessibilityService 时执行此操作:
@Override
public void onCreate() {
//Launch image capture intent for Color Contrast.
final Intent imageCaptureIntent = new Intent(this, ImageCaptureActivity.class);
imageCaptureIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
imageCaptureIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(imageCaptureIntent);
}
那么您的 ImageCaptureActivity 将只是一个标准 activity,但不会有任何 UI。它只会管理与媒体投影管理器的交互。在我的例子中,它最终是一个像素清晰的点。这实际上很难设置。我将复制我的 ImageCaptureActivity。这可能不会完全适合你,但当我深入研究这个问题时,我发现这个过程的记录非常糟糕。我没有对此进行篡改,但也许它会对你有所帮助。
public class ImageCaptureActivity extends AppCompatActivity {
private static final int REQUEST_MEDIA_PROJECTION = 1;
private MediaProjectionManager mProjectionManager;
private String mFileName;
private MediaProjection mMediaProjection = null;
private VirtualDisplay mVirtualDisplay;
private ImageReader mImageReader;
private static final int MAX_IMAGE_BUFFER = 10;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_capture);
mFileName = getFilesDir() + RuleColorContrast.IMAGE_CAPTURE_FILE_NAME;
OrientationChangedListener mOrientationChangedListener = new OrientationChangedListener(this);
mOrientationChangedListener.enable();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mProjectionManager = (MediaProjectionManager)getSystemService(MEDIA_PROJECTION_SERVICE);
startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (requestCode == REQUEST_MEDIA_PROJECTION) {
String message;
if (resultCode != Activity.RESULT_OK) {
message = "Media Projection Declined";
mMediaProjection = null;
} else {
message = "Media Projection Accepted";
mMediaProjection = mProjectionManager.getMediaProjection(resultCode, resultData);
attachImageCaptureOverlay();
}
Toast toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
toast.show();
finish();
}
}
private class OrientationChangedListener extends OrientationEventListener {
int mLastOrientation = -1;
OrientationChangedListener(Context context) {
super(context);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onOrientationChanged(int orientation) {
final int screenOrientation = getWindowManager().getDefaultDisplay().getRotation();
if (mVirtualDisplay == null) return;
if (mLastOrientation == screenOrientation) return;
mLastOrientation = screenOrientation;
detachImageCaptureOverlay();
attachImageCaptureOverlay();
}
}
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image == null || image.getPlanes().length <= 0) return;
final Image.Plane plane = image.getPlanes()[0];
final int rowPadding = plane.getRowStride() - plane.getPixelStride() * image.getWidth();
final int bitmapWidth = image.getWidth() + rowPadding / plane.getPixelStride();
final Bitmap tempBitmap = Bitmap.createBitmap(bitmapWidth, image.getHeight(), Bitmap.Config.ARGB_8888);
tempBitmap.copyPixelsFromBuffer(plane.getBuffer());
Rect cropRect = image.getCropRect();
final Bitmap bitmap = Bitmap.createBitmap(tempBitmap, cropRect.left, cropRect.top, cropRect.width(), cropRect.height());
//Do something with the bitmap
image.close();
}
};
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private void attachImageCaptureOverlay() {
if (mMediaProjection == null) return;
final DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
mImageReader = ImageReader.newInstance(metrics.widthPixels, metrics.heightPixels, PixelFormat.RGBA_8888, MAX_IMAGE_BUFFER);
mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCaptureTest",
metrics.widthPixels, metrics.heightPixels, metrics.densityDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(), null, null);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, null);
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void detachImageCaptureOverlay() {
mVirtualDisplay.release();
mImageReader.close();
}
}
注意:这种方法早在 Android 5.0.
就可以使用