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 ...
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. –
@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
@swk ¿Has encontrado alguna forma de convertir framebuffer en un archivo de imagen? –