¿Cómo tomar una captura de pantalla de forma global?

Antecedentes

Desde Android API 21, es posible que las aplicaciones tomen capturas de pantalla globalmente y graben la pantalla.

El problema

He hecho un código de muestra de todo lo que he encontrado en Internet, pero tiene algunos problemas:

Es bastante lento Tal vez sea posible evitarlo, al menos para múltiples capturas de pantalla, evitando la notificación de que se elimine hasta que realmente no sea necesario.Tiene márgenes negros a izquierda y derecha, lo que significa que algo podría estar mal con los cálculos:

Lo que he intentado

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private static final int REQUEST_ID = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.checkIfPossibleToRecordButton).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(final View v) {
                ScreenshotManager.INSTANCE.requestScreenshotPermission(MainActivity.this, REQUEST_ID);
            }
        });
        findViewById(R.id.takeScreenshotButton).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(final View v) {
                ScreenshotManager.INSTANCE.takeScreenshot(MainActivity.this);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_ID)
            ScreenshotManager.INSTANCE.onActivityResult(resultCode, data);
    }
}

layout / activity_main.xml

<LinearLayout
    android:id="@+id/rootView"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">


    <Button
        android:id="@+id/checkIfPossibleToRecordButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="request if possible"/>

    <Button
        android:id="@+id/takeScreenshotButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="take screenshot"/>

</LinearLayout>

ScreenshotManager

public class ScreenshotManager {
    private static final String SCREENCAP_NAME = "screencap";
    private static final int VIRTUAL_DISPLAY_FLAGS = DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
    public static final ScreenshotManager INSTANCE = new ScreenshotManager();
    private Intent mIntent;

    private ScreenshotManager() {
    }

    public void requestScreenshotPermission(@NonNull Activity activity, int requestId) {
        MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
        activity.startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), requestId);
    }


    public void onActivityResult(int resultCode, Intent data) {
        if (resultCode == Activity.RESULT_OK && data != null)
            mIntent = data;
        else mIntent = null;
    }

    @UiThread
    public boolean takeScreenshot(@NonNull Context context) {
        if (mIntent == null)
            return false;
        final MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) context.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
        final MediaProjection mediaProjection = mediaProjectionManager.getMediaProjection(Activity.RESULT_OK, mIntent);
        if (mediaProjection == null)
            return false;
        final int density = context.getResources().getDisplayMetrics().densityDpi;
        final Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        final Point size = new Point();
        display.getSize(size);
        final int width = size.x, height = size.y;

        // start capture reader
        final ImageReader imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 1);
        final VirtualDisplay virtualDisplay = mediaProjection.createVirtualDisplay(SCREENCAP_NAME, width, height, density, VIRTUAL_DISPLAY_FLAGS, imageReader.getSurface(), null, null);
        imageReader.setOnImageAvailableListener(new OnImageAvailableListener() {
            @Override
            public void onImageAvailable(final ImageReader reader) {
                Log.d("AppLog", "onImageAvailable");
                mediaProjection.stop();
                new AsyncTask<Void, Void, Bitmap>() {
                    @Override
                    protected Bitmap doInBackground(final Void... params) {
                        Image image = null;
                        Bitmap bitmap = null;
                        try {
                            image = reader.acquireLatestImage();
                            if (image != null) {
                                Plane[] planes = image.getPlanes();
                                ByteBuffer buffer = planes[0].getBuffer();
                                int pixelStride = planes[0].getPixelStride(), rowStride = planes[0].getRowStride(), rowPadding = rowStride - pixelStride * width;
                                bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Config.ARGB_8888);
                                bitmap.copyPixelsFromBuffer(buffer);
                                return bitmap;
                            }
                        } catch (Exception e) {
                            if (bitmap != null)
                                bitmap.recycle();
                            e.printStackTrace();
                        }
                        if (image != null)
                            image.close();
                        reader.close();
                        return null;
                    }

                    @Override
                    protected void onPostExecute(final Bitmap bitmap) {
                        super.onPostExecute(bitmap);
                        Log.d("AppLog", "Got bitmap?" + (bitmap != null));
                    }
                }.execute();
            }
        }, null);
        mediaProjection.registerCallback(new Callback() {
            @Override
            public void onStop() {
                super.onStop();
                if (virtualDisplay != null)
                    virtualDisplay.release();
                imageReader.setOnImageAvailableListener(null, null);
                mediaProjection.unregisterCallback(this);
            }
        }, null);
        return true;
    }
}
Las preguntas

Bueno, se trata de los problemas:

¿Por qué es tan lento? ¿Hay alguna manera de mejorarlo?¿Cómo puedo evitar, entre tomar capturas de pantalla, la eliminación de la notificación de ellos? ¿Cuándo puedo eliminar la notificación? ¿La notificación significa que toma capturas de pantalla constantemente?¿Por qué el mapa de bits de salida (actualmente no hago nada con él, porque todavía es POC) tiene márgenes negros? ¿Qué tiene de malo el código en este asunto?

NOTA: No quiero tomar una captura de pantalla solo de la aplicación actual. Quiero saber cómo usarlo globalmente, para todas las aplicaciones, lo que es posible oficialmente solo usando esta API, que yo sepa.

EDITAR: He notado que en el sitio web de CommonsWare (aquí), se dice que el mapa de bits de salida es más grande por alguna razón, pero a diferencia de lo que he notado (margen negro al principio Y al final), dice que se supone que debe estar al final:

Por razones inexplicables, será un poco más grande, con un exceso de píxeles no utilizados en cada fila al final.

He intentado lo que se ofreció allí, pero se bloquea con la excepción "java.lang.RuntimeException: Buffer no lo suficientemente grande para píxeles".

Respuestas a la pregunta(1)

Su respuesta a la pregunta