2

I used the accessibility service to expand the notifications bar on receiving notifications. I'm trying to take the screenshot of the Notification in the notification drawer.

From the documentation of the Accessibility service it possible to take the screenshot of the device from Android P only.

is there any other possibilities to take the screenshot of the notification drawer as my App is not in the foreground. It is running in the background

Cœur
  • 37,241
  • 25
  • 195
  • 267
Siva
  • 1,078
  • 4
  • 18
  • 36
  • you can use MediaProjection for that, it will help you to take screenshot from background. – Usman Rana Apr 06 '18 at 10:11
  • @UsmanRana I able to take the screenshot now using mediaprojection – Siva Apr 09 '18 at 13:34
  • @UsmanRana Can you answer this question on accessibility service to click notification - https://stackoverflow.com/questions/49734263/click-on-the-notification-using-accessibility-service-programmatically – Siva Apr 09 '18 at 13:35

1 Answers1

3

Yes, you can do this, though it's tricky. The trick is to utilize the Media Projection Manager combined with an Activity that is in the same package as your Service. You can then utilize the MediaProjectionManager's ability to capture images, along with shared storage, to grab screenshots.

In the on create of your AccessibilityService do this:

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

Then your ImageCaptureActivity will just be a standard activity, but won't have any UI. It will just manage the interactions with the Media Projection Manager. In my case, it ends up being a one pixel clear dot. This is actually pretty difficult to set up. I'll copy my ImageCaptureActivity. This probably won't completely work for you, but when I was digging into this, I found this process terribly poorly documented. I have not doctored this up, but maybe it will help you.

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

NOTE: This approach will work as far back as Android 5.0.

MobA11y
  • 18,425
  • 3
  • 49
  • 76
  • Thanks. Will this approach works for the Android Nougat and Oreo versions – Siva Apr 03 '18 at 16:25
  • You missed the line at the bottom didn't you? – MobA11y Apr 03 '18 at 16:59
  • I tried the above code and facing this error. DequeBitmap is not available. Can you update the implementation or what is this method doing – Siva Apr 04 '18 at 16:44
  • I mean, I feel like it's your responsibility to figure out what you want to do with your bitmap once you have it... – MobA11y Apr 05 '18 at 02:13
  • I'm using the following code to save the bitmap as screenshot: FileOutputStream outputStream = null; try { outputStream = new FileOutputStream(mFileName); int quality = 100; bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream); outputStream.flush(); outputStream.close(); } catch (IOException e) { e.printStackTrace(); } My image is a blank image or black screenshot. Even if I delete the file again there is a screenshot generated which is blank. I tried saving it as PNG also. but the same issue – Siva Apr 06 '18 at 07:23
  • 1
    After making some changes the above is code is working for me. thanks @chrisCM – Siva Apr 09 '18 at 06:37
  • can you answer this question on accessibility service to click notifications. https://stackoverflow.com/questions/49734263/click-on-the-notification-using-accessibility-service-programmatically – Siva Apr 09 '18 at 13:33