I'm trying to encapsulate taking a screen shot of the Android screen away from my primary activity. I have a class that implements
ImageReader.IOnImageAvailableListner
but
imageReader.SetOnImageAvailableListener(this, null)
in the class throws a cast exception (but not a compile exception). The only way I've found to avoid this is to have my activity itself implement IOnImageAvailableListner
. It seems that there is some part of Xamarin/Mono that really requires the argument to SetOnImageAvailableListner
to be of type Activity that implements IOnImageAvailableListner
.
here is the relevant section of my class:
public class Screenshooter : ImageReader.IOnImageAvailableListener
{
public void TakeScreenshot(Context context,
Result resultCode,
Intent data,
IOnScreenshot onScreenshotCallback)
{
_context = context;
_onScreenshot = onScreenshotCallback;
_imageAvailableCount = 0;
var size = new Point();
((Activity) context).WindowManager.DefaultDisplay.GetSize(size);
_width = size.X;
_height = size.Y;
_imageReader = ImageReader.NewInstance(_width, _height, ImageFormatType.Rgb565, 2);
MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) context.GetSystemService(Context.MediaProjectionService);
if (_mediaProjection == null)
{
_mediaProjection =mediaProjectionManager.GetMediaProjection((int) resultCode, data);
if (_mediaProjection == null)
{
// Log.e(TAG, "MediaProjection null. Cannot take the screenshot.");
logger.Error("MediaProjection null. Cannot take the screenshot.");
return;
}
}
try
{
_virtualDisplay = _mediaProjection.CreateVirtualDisplay("Screenshotter", _width, _height, (int) context .Resources.DisplayMetrics.DensityDpi, (DisplayFlags) DisplayManager.VirtualDisplayFlagAutoMirror, _imageReader.Surface, null, null);
///////////// THIS IS THE LINE THAT FAILS ////////////
_imageReader.SetOnImageAvailableListener(this, null);
///////////////////////////////////////////////////////////
}
catch (Exception ex)
{
logger.Error($"Error in {nameof(TakeScreenshot)}", ex);
throw;
}
return;
}
//IOnImageAvailableListener Members
public void OnImageAvailable(ImageReader reader)
{
// do stuff
}
...
}
I instantiate it in the activity that requests the screen shot with:
public class MainActivity : Activity
{
private void TakeScreenshot()
{
StartActivityForResult(((MediaProjectionManager) GetSystemService(MediaProjectionService)).CreateScreenCaptureIntent(),
REQUEST_CODE);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
Screenshooter.GetInstance().takeScreenShot(this, resultCode, data, callback);
}
The callback code that is called after the screenshot is completed is omitted.
I'm seeing Java android code examples of this pattern everywhere. (specifically, I've been looking at this example: https://github.com/omerjerk/Screenshotter)
I'm new to both Android and Xamarin, but have extensive experience in C#.
Is this a bug in Xamarin? Is there a work-around?
Thanks
No, it's not the only way, to avoid implement the interface on activity class, you're doing right to create your own class which implements this interface, for example:
The cast exception is caused by the use of
this
, which refers to theActivity
class, and this class doesn't implement the interface. If you're familiar with C#, you should be able to understand this, you may changethis
here to a new instance of your class:For code like:
C# don't have such anonymous inner class of java, you may refer to Can a C# anonymous class implement an interface?. That's why we created a class to implement the interface.
The reason why a lot of demos directly implement the interface on activity is that sometimes we need a return value from the listener to continue our logical work, you may check the Api design of Xamarin for Events and Listeners.