In this post you will see how to show Presentation on android virtual display. Two use-cases will be demonstrated in one simple android application:
How to create a virtual display rendering the content to a Surface
How to show a Presentation on this virtual display
To get the repository of Android Studio project:
git clone https://github.com/andronmobi/PresentationOnVirtualDisplay.git -b l01_virtualdisplay
Creating Virtual Display
A virtual display is created by createVirtualDisplay method of DisplayManager. In this method (among other parameters) we have to pass a surface to which the content of the virtual display will be rendered and flags varying the behaviour of the virtual display:
mDisplayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION |
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay("MyVirtualDisplay",
WIDTH, HEIGHT, DENSITY_DPI, surface, flags);
But the code snippet presented above wont work if your application is not system and doesn’t have some permissions (like CAPTURE_VIDEO_OUTPUT or CAPTURE_SECURE_VIDEO_OUTPUT) required for certain flags. That’s why we have to use MediaProjectionManager to ask a permission to user to “cast a screen” (to obtain MediaProjection which creates a virtual display):
mProjectionManager = (MediaProjectionManager) getSystemService(
Context.MEDIA_PROJECTION_SERVICE);
startActivityForResult(mProjectionManager.createScreenCaptureIntent(),
PERMISSION_CODE);
In case if a user accepts the permission to cast a screen
cast-screen-permission
it will be possible to obtain an instance of MediaProjection and to create a virtual display:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
mResultCode = resultCode;
mResultData = data;
if (requestCode != PERMISSION_CODE) {
Toast.makeText(this, "Unknown request code: " + requestCode,
Toast.LENGTH_SHORT).show();
return;
}
if (resultCode != RESULT_OK) {
Toast.makeText(this, "Screen Cast Permission Denied",
Toast.LENGTH_SHORT).show();
return;
}
Log.i(TAG, "Get media projection with the new permission");
mProjection = getProjection();
createVirtualDisplay();
}
private MediaProjection getProjection() {
MediaProjection projection = mProjectionManager.getMediaProjection(mResultCode,
mResultData);
// Add a callback to be informed if the projection
// will be stopped from the status bar.
mProjectionCallback = new MediaProjection.Callback() {
@Override
public void onStop() {
Log.d(TAG, "MediaProjection.Callback onStop obj:" + toString());
destroyVirtualDisplay();
mProjection = null;
}
};
projection.registerCallback(mProjectionCallback, null);
return projection;
}
private void createVirtualDisplay() {
if (mProjection != null && mVirtualDisplay == null) {
Log.d(TAG, "createVirtualDisplay WxH (px): " + mWidth + "x" + mHeight +
", dpi: " + mMetrics.densityDpi);
int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
//flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
mVirtualDisplay = mProjection.createVirtualDisplay("MyVirtualDisplay",
mWidth, mHeight, mMetrics.densityDpi, flags, mSurface,
null /*Callbacks*/, null /*Handler*/);
mButtonCreate.setEnabled(false);
mButtonDestroy.setEnabled(true);
}
}
private void destroyVirtualDisplay() {
Log.d(TAG, "destroyVirtualDisplay");
if (mVirtualDisplay != null) {
Log.d(TAG, "destroyVirtualDisplay release");
mVirtualDisplay.release();
mVirtualDisplay = null;
mButtonDestroy.setEnabled(false);
mButtonCreate.setEnabled(true);
}
}
Showing Presentation on Virtual Display
PresentationOnVirtualDisplay
Once a virtual display is created you can display a content on it. In our case, it will be a Presentation (android Dialog for secondary display) with LinearLayout as a content view and animated (rotating) TextView on it. To listen for changes in available display devices a DisplayListener must be registered:
mDisplayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
mDisplayManager.registerDisplayListener(mDisplayListener, null);
Then, after creating a virtual display the onDisplayAdded callback will be called. Here we register the number of display ID to create a new instance of MyPresentation class later in another callback – onDisplayChanged. The presentation will be dismissed in onDisplayRemoved if the virtual display showing it is removed.
private final DisplayManager.DisplayListener mDisplayListener =
new DisplayManager.DisplayListener() {
private boolean mNewDisplayAdded = false;
private int mCurrentDisplayId = -1;
private MyPresentation mPresentation;
@Override
public void onDisplayAdded(int i) {
Log.d(TAG, "onDisplayAdded id=" + i);
if (!mNewDisplayAdded && mCurrentDisplayId == -1) {
mNewDisplayAdded = true;
mCurrentDisplayId = i;
}
}
@Override
public void onDisplayRemoved(int i) {
Log.d(TAG, "onDisplayRemoved id=" + i);
if (mCurrentDisplayId == i) {
mNewDisplayAdded = false;
mCurrentDisplayId = -1;
if (mPresentation != null) {
mPresentation.dismiss();
mPresentation = null;
}
}
}
@Override
public void onDisplayChanged(int i) {
Log.d(TAG, "onDisplayChanged id=" + i);
if (mCurrentDisplayId == i) {
if (mNewDisplayAdded) {
// create a presentation
mNewDisplayAdded = false;
Display display = mDisplayManager.getDisplay(i);
mPresentation = new MyPresentation(MainActivity.this, display);
mPresentation.show();
}
}
}
};
MyPresentation inner static class:
private final static class MyPresentation extends Presentation {
public MyPresentation(Context context, Display display) {
super(context, display);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "MyPresentation onCreate");
setContentView(R.layout.my_presentation);
TextView tv = (TextView) findViewById(R.id.textView);
Animation myRotation = AnimationUtils.loadAnimation(getContext(), R.anim.rotator);
tv.startAnimation(myRotation);
}
}