2012-06-09 16 views
7

ACTUALIZACIÓN Hay una serie de otras publicaciones que preguntan cómo obtener una captura de pantalla en Android, pero ninguna parecía tener una respuesta completa de cómo hacerlo. Originalmente publiqué esto como una pregunta debido a un problema en particular que me estaba encontrando al intentar abrir una transmisión al Frame Buffer. Ahora cambié para eliminar el Frame Buffer a un archivo, así que actualicé mi publicación para mostrar cómo llegué allí. Para referencia (y reconocimiento), encontré el comando para enviar FrameBuffer a un archivo desde this post (desafortunadamente no me proporcionó cómo llegó a ese punto). Solo me falta cómo convertir los datos brutos que extraje de Frame Buffer en un archivo de imagen real.Captura de pantalla de Android en el dispositivo rooteado

Mi intención era realizar un volcado completo de la pantalla real en un dispositivo Android. El único que pude encontrar para hacerlo sin usar el puente de adb fue para acceder directamente al Buffer de Marco del sistema. Obviamente, este enfoque requerirá privilegios de root en el dispositivo y para la aplicación que lo ejecuta. Afortunadamente para mis propósitos, tengo control sobre cómo se configura el dispositivo y es factible tener el dispositivo rooteado con privilegios de raíz proporcionados a mi aplicación. Mi prueba se está realizando actualmente en un viejo Droid que ejecuta 2.2.3.

Encontré mis primeros indicios de cómo abordarlo desde https://stackoverflow.com/a/6970338/1446554. Después de investigar un poco más encontré otro artículo que describe cómo correctamente run shell commands as root. Lo estaban usando para ejecutar un reinicio, lo uso para enviar el búfer de cuadros actual a un archivo real. Mis pruebas actuales solo han llegado a hacer esto a través de ADB y en una Actividad básica (cada una de ellas provista como root). Haré más pruebas de un servicio que se ejecuta en segundo plano, ¡actualizaciones por venir! Aquí está mi actividad de toda prueba que pueda exportar la pantalla actual en un archivo:

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; 
     } 
    } 
} 

Esto también requiere la adición de la autorización android.permission.WRITE_EXTERNAL_STORAGE al Manifiesto. Como se sugiere en this post. De lo contrario, se ejecuta, no se queja, no crea los directorios ni el archivo.

Originalmente no pude obtener datos útiles del Frame Buffer debido a que no entendía cómo ejecutar correctamente los comandos de shell. Ahora que he cambiado a usar las secuencias para ejecutar comandos, puedo usar '>' para enviar los datos actuales de Frame Buffer a un archivo real ...

+1

Gracias por su codificación. Sin embargo, el archivo '.raw' podría tener un problema. Intenté abrir el archivo de imagen '.raw' con google picassa, pero decía que hay una parte que picassa no puede leer. ¿Sabes cuál es el problema? ¿Cómo puedo obtener un archivo de imagen normal de él? Gracias. –

+0

@swk Esta no es una pregunta fácil de responder. Mis requisitos estaban más en el ámbito de determinar las diferencias entre capturas de pantalla secuenciales, por lo que no he determinado completamente cómo convertirlas de nuevo en imágenes. Sugeriría una búsqueda exhaustiva del resto de Stack Overflow específicamente para convertir los datos encontrados en/dev/graphics/fb0 en una imagen. – cgolden

+0

@swk ¿Has encontrado alguna forma de convertir framebuffer en un archivo de imagen? –

Respuesta

3

Eso sería diferente para diferentes teléfonos. Depende del formato de gráficos subyacente de su dispositivo. Puede sondear qué formato de gráficos está usando las llamadas al sistema. Si solo va a ejecutar esto en dispositivos que conoce el formato de gráficos, puede escribir un convertidor que lo convierta en un formato conocido.

Puede echar un vistazo a la siguiente proyecto: http://code.google.com/p/android-fb2png/

Si nos fijamos en el código fuente de fb2png.c se puede ver que sondean FBIOGET_VSCREENINFO que contiene información sobre cómo el dispositivo almacena la imagen de la pantalla en la memoria . Una vez que lo sepa, debería poder convertirlo en un formato que pueda usar.

Espero que esto ayude.

+0

Esa parece la ruta más probable para resolver este problema. Como no parece haber más interés en este tema, estoy marcando esto como la respuesta. Sería bueno si alguien resolviera esto por completo, ¡pero desafortunadamente estoy abrumado con otras cosas ahora mismo! – cgolden

4

Una solución fácil para los dispositivos de ICS es el uso de lo siguiente desde la línea de comandos

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

Esto va a guardar el archivo screenshot.png en el directorio actual.

Probado en un Samsung Galaxy SII con la versión 4.0.3.

+0

Esta pregunta no estaba relacionada con el trabajo desde una máquina separada. Esto fue solo para un dispositivo, en la situación de campo. ADB es agradable, pero no me sirve de nada en esta situación. – cgolden

10

programación se puede ejecutar "adb shell/system/bin/screencap -p /sdcard/img.png" de la siguiente manera:

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

No funciona ... Es normal. incluso he establecido el permiso '' – ashish

+0

¿Qué versión de Android estás usando –

+0

su KitKat (4.4) @ Viswanath – ashish

Cuestiones relacionadas