Screenshot iOS come System.Drawing.Bitmap (Xamarin) dall’app di sfondo

Per un’applicazione privata (= da non pubblicare su qualsiasi appstore) su un dispositivo iOS jailbroken, è necessario che gli screenshot dell’intero schermo vengano trasmessi in background.

Domande simili sono state pubblicate

  • iOS: è ansible effettuare screenshot durante l’esecuzione come attività in background? che ha concluso che sarebbe imansible su dispositivi non jailbroken a causa della sandbox di sicurezza di iOS
  • Come prendere screenshot di iPhone in esecuzione dall’app di sfondo? è ancora in sospeso, ma sembra anche indirizzare i dispositivi non jailbroken
  • Come prendere screenshot di tutto lo schermo su un dispositivo iOS Jailbroken? (e in qualche modo simile iOS jailbroken che cattura gli screenshot dall’app in background ) sembra essere vicino a quello che sto cercando, ma il codice è scritto in Objective C e fa uso di una misteriosa funzione privata _UICreateScreenUIImage () di cui non so in quale modulo è definito (quindi non so come dichiararlo come funzione esterna). Anche lo screenshot viene restituito come UIImage (vedi http://developer.xamarin.com/api/type/MonoTouch.UIKit.UIImage/ ) che ancora non so come convertire in System.Drawing.Bitmap (vedi https://developer.xamarin.com/api/type/System.Drawing.Bitmap/ ).

Quello che segue è un post del mio codice. Sto cercando aiuto per l’implementazione di 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 _q; // Queue to store the Screenshots #region Interface ///  /// Creates a Screenshooter ///  /// Defines every how many MS a screenshot should be taken public ScreenshooterThread(Int32 intervalMS = 200) { _intervalMS = intervalMS; } ///  /// Starts taking Screenshots ///  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(); } } ///  /// Stops taking Screenshots ///  public void Stop() { if (!this.IsRunning) return; if (this.IsTerminating) return; lock(this) { _terminate = true; } } ///  /// Determines whether the Thread is currently running ///  public Boolean IsRunning { get { if (_t == null) return false; return _t.IsAlive; } } ///  /// Determines whether Thread-Termination has been invoked ///  public Boolean IsTerminating { get { return _terminate; } } ///  /// Get a Screenshot from the Queue. Returns null if none available ///  public Bitmap Deqeue() { lock(_q) { if (_q.Count < 1) return null; return _q.Dequeue(); } } #endregion #region Implementation Details ///  /// Add a Screenshot to the Queue ///  private void Enqueue(Bitmap b) { lock(_q) { _q.Enqueue(b); } } ///  /// Task-Implementation while running ///  private void DoWork() { while(!_terminate) { if (_intervalMS > 0) Thread.Sleep(_intervalMS); this.Enqueue(this.ShootScreen()); } this.CleanUp(); } ///  /// Takes a Screenshot of the device even if the app is in the Background ///  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(); } ///  /// Preparing for next launch ///  private void CleanUp() { lock(this) { _terminate = false; _t = null; } } #endregion } } 

PS: gli screenshot devono essere un System.Drawing.Bitmap perché devo eseguire operazioni Pixelwise su di esso in seguito

MODIFICA 1

Dopo ulteriori indagini ho trovato su http://sharpmobilecode.com/introduction-to-xamarin-what-it-is-what-it-isnt/ il seguente paragrafo

Ad esempio, supponiamo che tu abbia un codice C # esistente che utilizza System.Drawing.Bitmap. È una libreria comune che hai scritto in riferimento a più app desktop. Un ansible metodo assomiglia a questo:

 using System.Drawing; public Bitmap GetBitmapFromCache(string fileName) { return (Bitmap) Image.FromFile(fileName); } 

Se dovessi compilare questo metodo in un’app Xamarin.iOS o Xamarin.Android, si otterrebbe un errore di compilazione. Eh? Dopo ulteriori indagini, System.Drawing.Bitmap che System.Drawing.Bitmap e System.Drawing.Image non sono definiti. Che cosa!?!? Questa è una syntax C # valida! Dopo ulteriori indagini, si vedono solo alcune classi in System.Drawing effettivamente definite (RectangleF, PointF, ecc.). Dove diavolo è Bitmap e Immagine? Bene, c’è una buona ragione per cui queste classi non sono implementate nel Mono Framework. È perché non sono compatibili su più piattaforms.

Cosa significa esattamente? Diamo un’occhiata alla documentazione di System.Drawing su MSDN. Dichiara, “Lo spazio dei nomi System.Drawing fornisce accesso alle funzionalità grafiche di base GDI +.”. Cos’è GDI +? GDI + (Graphics Device Interface) è una libreria grafica (Win32) specifica per il sistema operativo Windows. Quindi, sotto il cofano di un System.Drawing.Bitmap, è un’implementazione Win32 che si collega ai sistemi operativi Windows Desktop / Server. Ciò significa che System.Drawing.Bitmap e System.Drawing.Image dipendono dalla piattaforma e funzionano solo su app desktop / server di Windows. Non sarai in grado di utilizzare questi corsi su app iOS, app Android o persino app Windows Phone. Quindi, come puoi utilizzare questo codice C # corrente in un’app per iOS o Android? Con una leggera modifica come questa:

 public byte[] GetBitmapFromCache(string fileName) { return File.ReadAllBytes(fileName); } 

iOS e Android non hanno alcun concetto di bitmap GDI + (System.Drawing.Bitmap), tuttavia, ogni piattaforma sa come convertire una matrice di byte nella propria implementazione di una bitmap. Utilizzando iOS, puoi creare un UIImage da una serie di byte e in Android puoi creare un Android.Graphics.Bitmap da quello stesso array.

Come indicato in http://sharpmobilecode.com/introduction-to-xamarin-what-it-is-what-it-isnt/ non è ansible creare un System.Drawing.Bitmap perché lo spazio dei nomi System.Drawing fornisce l’accesso a Funzionalità grafiche di base GDI + specifiche per il sistema operativo Windows e quindi semplicemente non disponibili su iOS.

È tuttavia ansible creare uno screenshot come UIImage come segue

  1. Come prendere screenshot di tutto lo schermo su un dispositivo iOS Jailbroken? ha mostrato che esiste una funzione privata _UICreateScreenUIImage(void) che restituisce lo screenshot desiderato. Per accedere a questa funzione, deve essere dichiarato

     using System.Runtime.InteropServices; using ObjCRuntime; [DLLImport(/*ObjCRuntime.*/Constants.UIKitLibrary)] extern static IntPtr _UICreateScreenUIImage(); 
  2. Quando chiamato, l’IntPtr restituito punta a una UIImage nativa e deve essere racchiusa in un object gestito, che viene eseguito da Runtime.GetNSObject(IntPtr)

     IntPtr ptr = _UICreateScreenUIImage(); UIImage uiimg = Runtime.GetNSObject(ptr) as UIImage; 

    L’ uiimg risultante contiene lo screenshot

L’immagine allegata contiene un esempio minimalista su come creare uno screenshot e salvarlo come .jpeg

Esempio minimalista su come creare e salvare uno screenshot

E un esempio di come potrebbe apparire uno screenshot quando l’app è in background

Immagine di come appare uno screenshot