Android:以编程方式拍摄通知栏的屏幕截图

Android:以编程方式拍摄通知栏的屏幕截图,android,android-activity,accessibilityservice,Android,Android Activity,Accessibilityservice,我使用可访问性服务来扩展接收通知时的通知栏。我正在尝试在通知抽屉中拍摄通知的屏幕截图 从易访问性服务的文档中,可以仅从Android p获取设备的屏幕截图 由于我的应用程序不在前台,是否还有其他可能拍摄通知抽屉的屏幕截图。它在后台运行是的,你可以这样做,尽管这很棘手。诀窍是将Media Projection Manager与服务包中的活动结合使用。然后,您可以利用MediaProjectionManager捕获图像的能力以及共享存储来抓取屏幕截图 在创建AccessibilityService时

我使用可访问性服务来扩展接收通知时的通知栏。我正在尝试在通知抽屉中拍摄通知的屏幕截图

从易访问性服务的文档中,可以仅从Android p获取设备的屏幕截图


由于我的应用程序不在前台,是否还有其他可能拍摄通知抽屉的屏幕截图。它在后台运行

是的,你可以这样做,尽管这很棘手。诀窍是将Media Projection Manager与服务包中的活动结合使用。然后,您可以利用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将只是一个标准活动,但没有任何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();
    }
}
公共类ImageCaptureActivity扩展了AppCompativity{
私有静态最终整数请求\媒体\投影=1;
私有媒体项目管理器mProjectionManager;
私有字符串mFileName;
私有媒体投影mmediprojection=null;
私有虚拟显示mVirtualDisplay;
私人图像阅读器;
私有静态最终int MAX_IMAGE_BUFFER=10;
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity\u image\u capture);
mFileName=getFilesDir()+RuleColorContrast.IMAGE\u CAPTURE\u FILE\u NAME;
OrientationChangedListener MorentationChangedListener=新的OrientationChangedListener(此);
MorentationChangedListener.enable();
if(Build.VERSION.SDK\u INT>=Build.VERSION\u code.LOLLIPOP){
mProjectionManager=(MediaProjectionManager)getSystemService(媒体投影服务);
startActivityForResult(mProjectionManager.createScreenCaptureIntent(),请求媒体投影);
}
}
@TargetApi(Build.VERSION\u code.LOLLIPOP)
ActivityResult上的公共void(int请求代码、int结果代码、意图结果数据){
if(requestCode==请求\媒体\投影){
字符串消息;
if(resultCode!=活动。结果\u确定){
message=“媒体放映被拒绝”;
mmediprojection=null;
}否则{
message=“接受媒体投影”;
mmediprojection=mProjectionManager.getMediaProjection(resultCode,resultData);
attachImageCaptureOverlay();
}
Toast Toast=Toast.makeText(this,message,Toast.LENGTH\u SHORT);
toast.show();
完成();
}
}
私有类OrientationChangedListener扩展了OrientationEventListener{
int MLA方向=-1;
方向更改列表器(上下文){
超级(上下文);
}
@RequiresApi(api=Build.VERSION\u code.LOLLIPOP)
@凌驾
公共无效onOrientationChanged(int方向){
final int screenOrientation=getWindowManager().getDefaultDisplay().getRotation();
if(mVirtualDisplay==null)返回;
if(mLastOrientation==screenOrientation)返回;
MLA方向=屏幕方向;
detachImageCaptureOverlay();
attachImageCaptureOverlay();
}
}
私有最终ImageReader.OnImageAvailableListener mOnImageAvailableListener=新ImageReader.OnImageAvailableListener(){
@RequiresApi(api=Build.VERSION\u code.LOLLIPOP)
@凌驾
公共图像可用(图像阅读器){
Image=reader.acquiredlatestimage();

if(image==null | | image.getPlanes().length谢谢。这种方法对Android Nougat和Oreo版本有效吗?你错过了底部的一行,不是吗?我尝试了上面的代码,遇到了这个错误。DequeBitmap不可用。你能更新实现吗?或者这个方法在做什么?我的意思是,我觉得你有责任弄清楚你想用y做什么一旦你有了我们的位图…在做了一些更改后,上面的代码对我有效。谢谢@chrisCMyou可以使用MediaProjection,它将帮助你从背景中截图。@UsmanRana我现在可以使用mediaprojection@UsmanRana你能回答这个关于无障碍服务的问题吗?点击通知n-