场景:
移动设备对移动存储媒介读取功能已经受限,但是Android SDK的权限依然开放且可供开发
例如:测试人员对软件功能点录屏截屏的证据无法直接通过数据线传输到电脑并上传到项目管理平台上。
声明:不可用于违反公司企业信息安全的操作,请确认操作是否合规,保证信息安全。
前提条件:
- Android SDK已经下载安装且配置到环境变量当中
- Python环境已安装PyQT5
import datetime
import subprocess
import sys
from PyQt5.QtCore import QTimer, pyqtSignal
from PyQt5.QtNetwork import QLocalSocket, QLocalServer
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QLineEdit, QPushButton, QMessageBox, QTextEdit
def is_directory(path):
result = subprocess.run(['adb', 'shell', f'if [ -d "{path}" ]; '
f'then echo "directory"; else echo "file"; fi'], stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
return result.stdout.decode('utf-8').strip() == 'directory'
class ADBPullApp(QWidget):
finished = pyqtSignal(bool, str)
def __init__(self):
super().__init__()
self.info_label = None
self.worker = None
self.file_name_input = None
self.files_display = None
self.refresh_button = None
self.folder_path_input = None
self.device_status_label = None
self.layout = None
self.pull_button = None
self.initUI()
self.timer = QTimer(self)
self.timer.timeout.connect(self.check_device_connection)
# 每2秒检测一次设备连接
self.timer.start(2000)
def initUI(self):
self.layout = QVBoxLayout()
self.device_status_label = QLabel('Checking device connection...')
self.layout.addWidget(self.device_status_label)
self.folder_path_input = QLineEdit(self)
self.folder_path_input.setText('/sdcard/DCIM/')
self.layout.addWidget(self.folder_path_input)
self.refresh_button = QPushButton('Refresh', self)
self.refresh_button.clicked.connect(self.refresh_files)
self.layout.addWidget(self.refresh_button)
self.files_display = QTextEdit(self)
self.files_display.setReadOnly(True)
self.layout.addWidget(self.files_display)
self.file_name_input = QLineEdit(self)
self.file_name_input.setPlaceholderText('Enter the file name in the folder')
self.layout.addWidget(self.file_name_input)
self.pull_button = QPushButton('Pull File', self)
self.pull_button.setEnabled(False)
self.pull_button.clicked.connect(self.pull_file)
self.layout.addWidget(self.pull_button)
self.info_label = QLabel('1.You must have adb tool in your device and already in environment!!!\n'
'2.The refresh list only show today\'s file.\n'
'3.Open developers model and turn on th USB debug of your android device.\n'
'4.Only can pull file from android device.')
self.layout.addWidget(self.info_label)
self.setLayout(self.layout)
self.setWindowTitle('PW File Puller')
self.show()
def check_device_connection(self):
result = subprocess.run(['adb', 'devices'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
devices_output = result.stdout.decode('utf-8')
if "device" in devices_output.split():
self.device_status_label.setText('Device connected.')
self.pull_button.setEnabled(True)
else:
self.device_status_label.setText('No device connected.')
self.pull_button.setEnabled(False)
def refresh_files(self):
folder_path = self.folder_path_input.text()
today = datetime.datetime.now().strftime('%Y-%m-%d')
cmd = f'adb shell "find {folder_path} -type f -newermt {today} 2>/dev/null"'
result = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
files_output = result.stdout.decode('utf-8')
self.files_display.setPlainText(files_output)
def pull_file(self):
folder_path = self.folder_path_input.text()
file_name = self.file_name_input.text()
if file_name:
full_path = f"{folder_path}/{file_name}"
else:
full_path = folder_path
if is_directory(full_path):
reply = QMessageBox.question(self, 'Confirm', 'The path is a directory. Do you want to '
'pull the entire directory?',
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.No:
return
self.pull_button.setEnabled(False)
result = subprocess.run(['adb', 'pull', full_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if result.returncode == 0:
QMessageBox.information(self, 'Success', 'File pulled successfully to current directory.\n'
'Please wait some seconds if it\'s not appear.')
elif full_path is None:
QMessageBox.warning(self, 'Warn', 'The path is empty')
else:
QMessageBox.critical(self, 'Error', f'Failed to pull file: {result.stderr.decode("utf-8")}')
if __name__ == '__main__':
app = QApplication(sys.argv)
try:
serverName = 'PWPULLFILE'
socket = QLocalSocket()
# 建立socket,防止应用多开
socket.connectToServer(serverName)
if socket.waitForConnected(500):
app.quit()
else:
localServer = QLocalServer()
localServer.listen(serverName)
ex = ADBPullApp()
finally:
sys.exit(app.exec_())
效果:
功能点:
- 刷新递归查询当前路径及子目录下所有今天创建的文件,且显示在列表中供用户复制粘贴实现特定拉取
- 如果只输入了文件夹路径,则会拉取整个文件夹到当前所在路径中
- 如果输入了文件夹下的特定文件则只拉取文件
- 随插随拔,自动检测设备是否连接