Android tirar screenshot no dispositivo enraizado
ATUALIZAR Há um número de outros posts perguntando como obter uma captura de tela no android, mas nenhum parecia ter uma resposta completa de como fazê-lo. Originalmente eu postei isso como uma questão devido a um problema específico que eu estava correndo ao tentar abrir um fluxo para o buffer de quadros. Agora eu troquei para jogar o buffer de quadros em um arquivo, então atualizei meu post para mostrar como cheguei lá. Para referência (e reconhecimento), encontrei o comando para enviar o FrameBuffer para um arquivo deesta postagem (infelizmente ele não explicou como chegou a esse ponto). Eu só estou perdendo como transformar os dados brutos que eu tirei do buffer de quadros em um arquivo de imagem real.
Minha intenção era tirar um despejo completo da tela real em um dispositivo Android. osó A maneira que eu poderia encontrar para fazer isso sem usar o AdB Bridge era acessar diretamente o buffer de quadros do sistema. Obviamente, essa abordagem exigirá privilégios de root no dispositivo e para o aplicativo que o executa! Felizmente, para os meus propósitos, eu tenho controle sobre como o dispositivo está configurado e ter o dispositivo com privilégios de root fornecidos para o meu aplicativo é viável. Meu teste está sendo feito atualmente em um antigo Droid rodando 2.2.3.
Eu encontrei minhas primeiras dicas de como abordá-lohttps://stackoverflow.com/a/6970338/1446554. Depois de pesquisar um pouco mais, encontrei outro artigo que descreve comoexecutar comandos shell como root. Eles estavam usando para executar uma reinicialização, eu usá-lo para enviar o buffer de quadros atual para um arquivo real. Meu teste atual só chegou a fazer isso via ADB e em uma atividade básica (cada um sendo fornecido root). Eu vou estar fazendo mais testes de um serviço em execução em segundo plano, atualizações para vir! Aqui está toda minha atividade de teste que pode exportar a tela atual para um arquivo:
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;
}
}
}
Isso também requer a adição deandroid.permission.WRITE_EXTERNAL_STORAGE
permissão para o Manifesto. Como sugerido emesta postagem. Caso contrário, ele é executado, não reclama, não cria os diretórios nem o arquivo.
Originalmente, não consegui obter dados utilizáveis do Buffer de quadros devido a não entender como executar corretamente os comandos do shell. Agora que eu troquei para usar os fluxos para executar comandos, eu posso usar '>' para enviar os dados atuais do Frame Buffer para um arquivo atual ...