2012-06-09 7 views
7

UPDATE Ci sono un certo numero di altri post che chiedono come ottenere uno Screenshot in Android ma nessuno sembra avere una risposta completa su come farlo. Originariamente l'ho postato come una domanda a causa di un particolare problema che stavo incontrando mentre tentavo di aprire un flusso sul Frame Buffer. Ora mi sono scambiato per scaricare il Frame Buffer in un file, quindi ho aggiornato il mio post per mostrare come sono arrivato. Per riferimento (e riconoscimento), ho trovato il comando per inviare il FrameBuffer a un file da this post (sfortunatamente non ha fornito come è arrivato a quel punto). Mi manca solo come trasformare i dati grezzi che ho tirato da Frame Buffer in un vero e proprio file immagine.Android cattura screenshot sul dispositivo rooted

La mia intenzione era di fare un dump completo dello schermo attuale su un dispositivo Android. Il modo solo in cui ho potuto trovare di farlo senza usare il bridge adb era di accedere direttamente al Frame Buffer del sistema. Ovviamente questo approccio richiederà i privilegi di root sul dispositivo e per l'app che lo esegue! Fortunatamente per i miei scopi ho il controllo su come è configurato il Dispositivo e avere il dispositivo rootato con i privilegi di root forniti alla mia applicazione è fattibile. Il mio test è attualmente in corso su un vecchio Droid in esecuzione 2.2.3.

Ho trovato i miei primi suggerimenti su come accedervi da https://stackoverflow.com/a/6970338/1446554. Dopo un po 'di ricerche ho trovato un altro articolo che descrive come correttamente run shell commands as root. Lo stavano usando per eseguire un riavvio, lo uso per inviare il buffer del frame corrente a un file reale. Il mio attuale test è solo arrivato a fare questo tramite ADB e in un'attività di base (ognuno è fornito come root). Farò ulteriori test da un servizio in esecuzione in background, gli aggiornamenti a venire! Qui è tutta la mia attività di test in grado di esportare la schermata corrente in un file:

public class ScreenshotterActivity extends Activity { 
    public static final String TAG = "ScreenShotter"; 

    private Button _SSButton; 
    private PullScreenAsyncTask _Puller; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 


     _SSButton = (Button)findViewById(R.id.main_screenshotButton); 
     _SSButton.setOnClickListener(new OnClickListener() { 

      public void onClick(View v) { 
       if (_Puller != null) 
        return; 
       //TODO: Verify that external storage is available! Could always use internal instead... 

       _Puller = new PullScreenAsyncTask(); 
       _Puller.execute((Void[])null); 
      } 
     }); 
    } 

    private void runSuShellCommand(String cmd) { 
     Runtime runtime = Runtime.getRuntime(); 
     Process proc = null; 
     OutputStreamWriter osw = null; 
     StringBuilder sbstdOut = new StringBuilder(); 
     StringBuilder sbstdErr = new StringBuilder(); 

     try { // Run Script 
      proc = runtime.exec("su"); 
      osw = new OutputStreamWriter(proc.getOutputStream()); 
      osw.write(cmd); 
      osw.flush(); 
      osw.close(); 
     } catch (IOException ex) { 
      ex.printStackTrace(); 
     } finally { 
      if (osw != null) { 
       try { 
        osw.close(); 
       } catch (IOException e) { 
        e.printStackTrace();      
       } 
      } 
     } 

     try { 
      if (proc != null) 
       proc.waitFor(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

     sbstdOut.append(readBufferedReader(new InputStreamReader(proc.getInputStream()))); 
     sbstdErr.append(readBufferedReader(new InputStreamReader(proc.getErrorStream()))); 
    } 

    private String readBufferedReader(InputStreamReader input) { 

     BufferedReader reader = new BufferedReader(input); 
     StringBuilder found = new StringBuilder(); 
     String currLine = null; 
     String sep = System.getProperty("line.separator"); 
     try { 
      // Read it all in, line by line. 
      while ((currLine = reader.readLine()) != null) { 
       found.append(currLine); 
       found.append(sep); 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } finally { 
      try { 
       reader.close(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 

     return null; 
    } 

    class PullScreenAsyncTask extends AsyncTask<Void, Void, Void> { 

     @Override 
     protected Void doInBackground(Void... params) { 

      File ssDir = new File(Environment.getExternalStorageDirectory(), "/screenshots"); 
      if (ssDir.exists() == false) { 
       Log.i(TAG, "Screenshot directory doesn't already exist, creating..."); 
       if (ssDir.mkdirs() == false) { 
        //TODO: We're kinda screwed... what can be done? 
        Log.w(TAG, "Failed to create directory structure necessary to work with screenshots!"); 
        return null; 
       } 
      } 
      File ss = new File(ssDir, "ss.raw");    
      if (ss.exists() == true) { 
       ss.delete(); 
       Log.i(TAG, "Deleted old Screenshot file."); 
      } 
      String cmd = "/system/bin/cat /dev/graphics/fb0 > "+ ss.getAbsolutePath(); 
      runSuShellCommand(cmd); 
      return null; 
     } 

     @Override 
     protected void onPostExecute(Void result) { 
      super.onPostExecute(result); 
      _Puller = null; 
     } 
    } 
} 

Questo richiede anche l'aggiunta del android.permission.WRITE_EXTERNAL_STORAGE permesso per il Manifesto. Come suggerito in this post. Altrimenti gira, non si lamenta, non crea le directory né il file.

In origine non è stato possibile ottenere dati utilizzabili dal buffer di frame a causa della mancata comprensione di come eseguire correttamente i comandi della shell. Ora che ho scambiato per utilizzare gli stream per l'esecuzione di comandi, posso usare '>' per inviare i dati correnti del Frame Buffer a un file reale ...

+1

Grazie per la vostra codifica. Tuttavia, il file '.raw' potrebbe avere un problema. Ho provato ad aprire il file di immagine '.raw' con google picassa, ma ha detto che c'è una parte in cui Picassa non può leggere. Sai qual è il problema? Come posso ottenere un file immagine normale da esso? Grazie. –

+0

@swk Questa non è una domanda semplice a cui rispondere. Le mie esigenze erano più nel regno di determinare le differenze tra le schermate in sequenza, quindi non ho ancora pienamente determinato come trasformarle in immagini! Suggerirei una ricerca approfondita del resto di Stack Overflow specificatamente per come trasformare i dati trovati in/dev/graphics/fb0 in un'immagine. – cgolden

+0

@swk Hai trovato un modo per coprire il framebuffer in un file immagine ?? –

risposta

3

Questo sarebbe diverso per i diversi telefoni. Dipende dal formato grafico di base del tuo dispositivo. È possibile interrogare il formato grafico utilizzando le chiamate di sistema. Se si esegue solo questo su dispositivi che si conosce il formato grafico di voi è possibile scrivere un convertitore che lo trasforma in un formato noto.

Si può avere uno sguardo al seguente progetto: http://code.google.com/p/android-fb2png/

Se si guarda il codice sorgente per fb2png.c si può vedere che il polling FBIOGET_VSCREENINFO che contiene informazioni su come il dispositivo memorizza l'immagine sullo schermo in memoria . Una volta che lo sai, dovresti essere in grado di convertirlo in un formato che puoi usare.

Spero che questo aiuti.

+0

Sembra il percorso più probabile per risolvere questo problema. Poiché non sembra esserci alcun ulteriore interesse in questo argomento, sto contrassegnando questo come la risposta. Sarebbe davvero bello se qualcuno lo risolvesse completamente, ma io sono sfortunatamente piuttosto sommerso da altre cose adesso! – cgolden

4

Una soluzione facile per i dispositivi ICS è quello di utilizzare il seguente dalla riga di comando

adb shell /system/bin/screencap -p /sdcard/screenshot.png 
adb pull /sdcard/screenshot.png screenshot.png 

Questo ti salvare il file screenshot.png nella directory corrente.

Testato su un Samsung Galaxy SII in esecuzione 4.0.3.

+0

Questa domanda non era correlata al funzionamento da una macchina separata. Questo era puramente per un dispositivo, nella situazione sul campo. ADB è bello, ma non mi fa nulla di buono in questa situazione. – cgolden

10

livello di programmazione è possibile eseguire "adb shell/system/bin/screencap -p /sdcard/img.png", come di seguito:

Process sh = Runtime.getRuntime().exec("su", null,null); 
OutputStream os = sh.getOutputStream(); 
os.write(("/system/bin/screencap -p " + "/sdcard/img.png").getBytes("ASCII")); 
os.flush(); 
os.close(); 
sh.waitFor(); 
+0

Non funziona .. Restituisce NULL. anche io ho impostato il permesso '' – ashish

+0

Quale versione di Android stai usando –

+0

il suo KitKat (4.4) @ Viswanath – ashish