Python + ADB

0 Flares Twitter 0 Facebook 0 Filament.io 0 Flares ×

Recentemente fiz um projeto para efetuar testes em dispositivos android utilizando a linguagem python. Esses testes eram feitos em um celular da samsung conectados via usb a um computador com windows e os dispositivos era testados utilizando um comando chamado adb (Android Debug Bridge).

Todos os meus outros posts foram feitos utilizando Linux, esse eu vou fazer utilizando o windows pois quero replicar o mesmo ambiente do cliente e mostar que o python também roda muito bem em ambientes windows.

O primeiro passo é instalar um cara chamado android studio, pois nele já tem o sdkmanager o adb e todos os requisitos necessários para o desenvolvimento android.

Para instalar é só baixar no site oficial:

https://developer.android.com/studio/install.html

Após fazer a instalação eu coloquei o caminho do adb dentro da variavel PATH do windows.

C:\Users\alisson\AppData\Local\Android\sdk\platform-tools

Não manjo nada de windows, mas no windows 10 é só abrir o iniciar e digitar variáveis e ele vai aparecer uma opção chamada: Editar variáveis de ambiente do sistema.

E lá vai ter a PATH e é só adicionar esse caminho lá, lembrando que tem que mudar o nome do usuário.

Feito isso abra o cmd do windows e digite o comando:

C:\Users\alisson>adb devices
 
List of devices attached

A saída será a de cima.

Então conecte o seu celular no computador e no gerenciamento de conexão USB marque a opção Depuração USB.

Desconecte o USB e conecte de novo e no seu celular irá aparecer uma mensagem para você aceitar a chave do computador.

Aceite e o celular já irá aparecer na lista de devices.

C:\Users\alisson>adb devices
 
List of devices attached
 
4012e40b        device

O meu celular é esse 4012e40b.

Caso eu queira entrar dentro do dispositivo é só digitar o seguinte comando:

C:\Users\alisson>adb shell
shell@A6020l36:/ $

eai já era, você está dentro do sistema android, pra quem conhece linux muitos dos comandos vão funcionar, como o ls, cd, pwd e entre outros comandos básicos.

Pra voltar ao cmd é só pressionar CTRL+D

Bom isso é o básico que é necessário saber por enquanto, pois isso permite saber que o seu celular está corretamente configurado para que sejam efetuados os testes.

Nos testes que foram feitos na empresa, utilizamos basicamente 3 módulos do python, sendo eles:

  • subprocess ( Executa comandos do sistema )
  • ElementTree ( Modulo para trabalhar com XML )
  • threading ( Modulo de threads para executar testes em paralelo )

No exemplo abaixo não vou usar threads, mas caso vocês precisem eu tenho um post especifico falando de threads neste link ( Threads em Python ).

O script completo é esse:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import subprocess
import xml.etree.ElementTree as ET
import time
 
 
def get_devices():
    output = subprocess.Popen('adb devices', shell=True, stdout=subprocess.PIPE).communicate()[0]
    output = str(output).split('attached')[1]
    output = output.split('device')[0]
    output = output.replace("\\n", "").replace("\\r", "")
    output = output.replace("\\t", "")
    return output
 
def get_screen(serial):
    output = subprocess.Popen('adb -s %s shell uiautomator dump'%serial,shell=True,stdout=subprocess.PIPE).communicate()[0]
    print(output)
    output = subprocess.Popen('adb -s %s pull /storage/sdcard0/window_dump.xml'%serial,shell=True,stdout=subprocess.PIPE).communicate()[0]
    print(output)
 
 
def tap_screen(app_name,x,y):
    print("Abrindo %s"%app_name)
    output = subprocess.Popen("adb shell input tap %s %s"%(x,y),shell=True,stdout=subprocess.PIPE)
    time.sleep(5)
    print("Voltando a tela anterior")
    output = subprocess.Popen("adb shell input keyevent 4",shell=True,stdout=subprocess.PIPE)
 
def test_app(node,app_name):
    if node.attrib.get('text') == app_name:            
        position = node.attrib.get('bounds').split(']')[0]
        position = position.replace("[","").split(",")
        print("Testando APP")
        tap_screen(app_name, position[0], position[1])
    else:   
        for n in node.findall('node'):                
            test_app(n,app_name)
 
def get_apps():
    xml = ET.parse('window_dump.xml')
    root = xml.getroot()
    app = test_app(root,'Play Store')    
 
 
 
device = get_devices()
get_screen(device)
get_apps()

Agora explicando o que isso faz.

A função get_devices abaixo:

1
2
3
4
5
6
7
def get_devices():
    output = subprocess.Popen('adb devices', shell=True, stdout=subprocess.PIPE).communicate()[0]
    output = str(output).split('attached')[1]
    output = output.split('device')[0]
    output = output.replace("\\n", "").replace("\\r", "")
    output = output.replace("\\t", "")
    return output

Ela executa um comando dentro do sistema operacional, o comando adb devices vai listar todos os seus dispositivos conectados, então a saída é armazenada dentro da variavel output e na sequencia vão sendo aplicados alguns comandos para remover caracteres especiais como quebra de linha e tabs para que no final venha só o SERIAL do dispositivo conectado.

Uma vez que tenho esse serial, é possível pegar um dump da tela para saber a posição dos icones dentro dela.

A tela que eu fiz um dump foi a seguinte:

O script criado vai procurar o botão da play store, clicar nele e depois voltar para essa mesma tela.

Para que isso aconteça foi criada a função get_screen.

1
2
3
4
5
def get_screen(serial):
    output = subprocess.Popen('adb -s %s shell uiautomator dump'%serial,shell=True,stdout=subprocess.PIPE).communicate()[0]
    print(output)
    output = subprocess.Popen('adb -s %s pull /storage/sdcard0/window_dump.xml'%serial,shell=True,stdout=subprocess.PIPE).communicate()[0]
    print(output)

Essa função roda o comando adb shell uiatomator dump, que gera um xml com as posições do icones na tela, uma vez que esse xml foi gerado ele é copiado para o diretorio onde está sendo executado o script com o comando adb pull.

Logo na sequencia é executada a função get_apps().

1
2
3
4
def get_apps():
    xml = ET.parse('window_dump.xml')
    root = xml.getroot()
    app = test_app(root,'Play Store')

Essa função basicamente lê o arquivo xml gerado pelo adb faz o parse dele de string para xml e manda para a função test_app.

1
2
3
4
5
6
7
8
9
def test_app(node,app_name):
    if node.attrib.get('text') == app_name:            
        position = node.attrib.get('bounds').split(']')[0]
        position = position.replace("[","").split(",")
        print("Testando APP")
        tap_screen(app_name, position[0], position[1])
    else:   
        for n in node.findall('node'):                
            test_app(n,app_name)

Essa função verifica se o elemento atual do xml possui um atributo chamado text e se esse atributo é igual ao nome do app procurado, que no caso é Play Store, caso esse app não seja encontrado é realizado um for buscando os elementos filhos do elemento atual, fazendo assim uma recursividade até que sejam percorridos todos os elementos filhos desse xml.

Quando o APP é encontrado, é pegado o valor do atributo bounds que guarda a posição do item na tela, essa posição é guarda na variável position e depois é chamada a função tap_screen que clica no botão.

1
2
3
4
5
6
def tap_screen(app_name,x,y):
    print("Abrindo %s"%app_name)
    output = subprocess.Popen("adb shell input tap %s %s"%(x,y),shell=True,stdout=subprocess.PIPE)
    time.sleep(5)
    print("Voltando a tela anterior")
    output = subprocess.Popen("adb shell input keyevent 4",shell=True,stdout=subprocess.PIPE)

Essa função recebe como parâmetros, o nome do aplicativo somente para que seja informado o nome na tela e as posições x e y do botão, essas posição são passadas para o comando adb shell input tap que vai clicar no play store, como esse app demora para abrir é aguardado o tempo de 5 segundos e depois é executado o comando adb shell input keyvent 4, que é o equivalente ao botão voltar do android, então é voltado para a tela anterior.

Alguns dos eventos possíveis são:

0 –> “KEYCODE_UNKNOWN”
1 –> “KEYCODE_MENU”
2 –> “KEYCODE_SOFT_RIGHT”
3 –> “KEYCODE_HOME”
4 –> “KEYCODE_BACK”
5 –> “KEYCODE_CALL”
6 –> “KEYCODE_ENDCALL”
7 –> “KEYCODE_0”
8 –> “KEYCODE_1”
9 –> “KEYCODE_2”
10 –> “KEYCODE_3”
11 –> “KEYCODE_4”
12 –> “KEYCODE_5”
13 –> “KEYCODE_6”
14 –> “KEYCODE_7”
15 –> “KEYCODE_8”
16 –> “KEYCODE_9”
17 –> “KEYCODE_STAR”
18 –> “KEYCODE_POUND”
19 –> “KEYCODE_DPAD_UP”
20 –> “KEYCODE_DPAD_DOWN”
21 –> “KEYCODE_DPAD_LEFT”
22 –> “KEYCODE_DPAD_RIGHT”
23 –> “KEYCODE_DPAD_CENTER”
24 –> “KEYCODE_VOLUME_UP”
25 –> “KEYCODE_VOLUME_DOWN”
26 –> “KEYCODE_POWER”
27 –> “KEYCODE_CAMERA”
28 –> “KEYCODE_CLEAR”
29 –> “KEYCODE_A”
30 –> “KEYCODE_B”
31 –> “KEYCODE_C”
32 –> “KEYCODE_D”
33 –> “KEYCODE_E”
34 –> “KEYCODE_F”
35 –> “KEYCODE_G”

Basicamente é isso ai, qualquer dúvida é só em dar um salve.

0 Flares Twitter 0 Facebook 0 Filament.io 0 Flares ×
9 meses ago

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *