For a private app (=not to be published on any appstore) on a jailbroken iOS-Device I need to take screenshots of the entire screen from being sent to the background.
Similar questions have been posted
- iOS: Is it possible to take screenshots while running as a background task? which concluded that it would be impossible on non-jailbroken devices due to iOS's security sandbox
- How to take screenshots of running iPhone from background app? is yet pending, but also seems to target non-jailbroken devices
- How to take screenshot of entire screen on a Jailbroken iOS Device? (and somewhat similar jailbroken iOS taking screenshot from background app ) seems to be close to what I am looking for, but the code is written in Objective C and makes use of a mysterious private function _UICreateScreenUIImage() of which I don't know in which module it is defined (so I don't know how to declare it as an external function). Also the screenshot is returned as a UIImage (see http://developer.xamarin.com/api/type/MonoTouch.UIKit.UIImage/ ) which I yet don't know how to convert to a System.Drawing.Bitmap (see https://developer.xamarin.com/api/type/System.Drawing.Bitmap/ ).
The following is a post of my code. I am looking for help with the implementation of private System.Drawing.Bitmap ShootScreen()
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
namespace StackOverflowSnippets
{
public sealed class ScreenshooterThread
{
private Thread _t = null; // Thread-Object: Execution-Helper
private Boolean _terminate = false; // Stop current execution?
private Int32 _intervalMS = 0; // Take Screenshots every ? MS
private Queue<Bitmap> _q; // Queue to store the Screenshots
#region Interface
/// <summary>
/// Creates a Screenshooter
/// </summary>
/// <param name="intervalMS">Defines every how many MS a screenshot should be taken</param>
public ScreenshooterThread(Int32 intervalMS = 200)
{
_intervalMS = intervalMS;
}
/// <summary>
/// Starts taking Screenshots
/// </summary>
public void Run()
{
if(this.IsRunning || this.IsTerminating)
{
throw new InvalidOperationException("ScreenshooterThread is currently running or terminating");
}
lock (this)
{
_terminate = false;
_t = new Thread(this.DoWork);
_t.Start();
}
}
/// <summary>
/// Stops taking Screenshots
/// </summary>
public void Stop()
{
if (!this.IsRunning) return; if (this.IsTerminating) return;
lock(this)
{
_terminate = true;
}
}
/// <summary>
/// Determines whether the Thread is currently running
/// </summary>
public Boolean IsRunning
{
get
{
if (_t == null) return false;
return _t.IsAlive;
}
}
/// <summary>
/// Determines whether Thread-Termination has been invoked
/// </summary>
public Boolean IsTerminating
{
get
{
return _terminate;
}
}
/// <summary>
/// Get a Screenshot from the Queue. Returns null if none available
/// </summary>
public Bitmap Deqeue()
{
lock(_q)
{
if (_q.Count < 1) return null;
return _q.Dequeue();
}
}
#endregion
#region Implementation Details
/// <summary>
/// Add a Screenshot to the Queue
/// </summary>
private void Enqueue(Bitmap b)
{
lock(_q)
{
_q.Enqueue(b);
}
}
/// <summary>
/// Task-Implementation while running
/// </summary>
private void DoWork()
{
while(!_terminate)
{
if (_intervalMS > 0) Thread.Sleep(_intervalMS);
this.Enqueue(this.ShootScreen());
}
this.CleanUp();
}
/// <summary>
/// Takes a Screenshot of the device even if the app is in the Background
/// </summary>
private System.Drawing.Bitmap ShootScreen()
{
// System.Drawing.Bitmap supported by Xamarin
// https://developer.xamarin.com/api/type/System.Drawing.Bitmap/
// Method in ObjC: https://stackoverflow.com/questions/33369014/how-to-take-screenshot-of-entire-screen-on-a-jailbroken-ios-device
throw new NotImplementedException();
}
/// <summary>
/// Preparing for next launch
/// </summary>
private void CleanUp()
{
lock(this)
{
_terminate = false;
_t = null;
}
}
#endregion
}
}
PS: The screenshots need to be a System.Drawing.Bitmap
because I need to perform Pixelwise operations on it afterwards
EDIT 1
Upon further investigation I found on http://sharpmobilecode.com/introduction-to-xamarin-what-it-is-what-it-isnt/ the following paragraph
For example, let’s say you have existing C# code that uses System.Drawing.Bitmap. It’s a common library you wrote that multiple Desktop apps reference. A possible method looks something like this:
using System.Drawing; public Bitmap GetBitmapFromCache(string fileName) { return (Bitmap) Image.FromFile(fileName); }
If you were to compile this method in a Xamarin.iOS or Xamarin.Android app,you would get a compile error. Huh? Upon further investigation, you would see that
System.Drawing.Bitmap
andSystem.Drawing.Image
are not define. What!?!? That’s valid C# syntax! Upon further investigation, you only see a few classes inSystem.Drawing
are actually defined (RectangleF, PointF, etc). Where the heck is Bitmap and Image? Well, there is a good reason these classes are not implemented in the Mono Framework. It’s because they are not cross platform compatible.What does that mean exactly? Let’s take a look at the System.Drawing documentation on MSDN. It states, “The System.Drawing namespace provides access to GDI+ basic graphics functionality.”. What is GDI+? GDI+ (Graphics Device Interface) is a graphics library (Win32) that is specific to the Windows Operating System. So underneath the hood of a System.Drawing.Bitmap, is a Win32 implementation that ties into the Windows Desktop/Server Operating systems. That means System.Drawing.Bitmap and System.Drawing.Image are platform dependent, and only work on Windows Desktop/Server apps. You will not be able to use these classes on iOS app, Android apps, or even Windows Phone apps. So how can you use this current C# code in an iOS or Android app? With a slight modification like this:
public byte[] GetBitmapFromCache(string fileName) { return File.ReadAllBytes(fileName); }
iOS and Android have no concept of GDI+ bitmap (System.Drawing.Bitmap), however, each platform knows how to convert an array of bytes into their own implementation of a bitmap. Using iOS, you can create a UIImage from an array of bytes, and in Android you can create an Android.Graphics.Bitmap from that same array.