Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/app/data/repositories/android_adb.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ADB File Explorer
# Copyright (C) 2022 Azat Aldeshov
import os
import threading
from typing import List

from app.core.configurations import Settings
Expand Down Expand Up @@ -116,13 +118,53 @@ def call(self, data: str):
elif data:
self.messages.append(data)

@staticmethod
def _remote_file_size(source: str) -> int:
try:
command = shlex.join(['stat', '-c', '%s', source])
response = adb.shell(ADBManager.get_device().id, [command])
if response.IsSuccessful and response.OutputData:
return int(response.OutputData.strip())
except (ValueError, AttributeError, TypeError):
pass
return 0

@staticmethod
def _poll_download_progress(dest_file, total, callback, name, stop_event):
# adb pull only prints `[ N%]` progress to a TTY; when its output is
# piped (as here) it stays silent until the transfer finishes, so the
# progress bar never moved. Derive progress from the size of the file
# being written locally instead.
while not stop_event.wait(0.25):
try:
current = os.path.getsize(dest_file)
except OSError:
continue
callback(name, min(int(current * 100 / total), 99))

@classmethod
def download(cls, progress_callback: callable, source: str, destination: str) -> (str, str):
if not destination:
destination = Settings.device_downloads_path(ADBManager.get_device())
if ADBManager.get_device() and source and destination:
helper = cls.UpDownHelper(progress_callback)

name = source.rstrip('/').rsplit('/', 1)[-1]
total = cls._remote_file_size(source)
dest_file = os.path.join(destination, name) if os.path.isdir(destination) else destination
stop_event = threading.Event()
if total > 0 and progress_callback:
threading.Thread(
target=cls._poll_download_progress,
args=(dest_file, total, progress_callback, name, stop_event),
daemon=True,
).start()

response = adb.pull(ADBManager.get_device().id, source, destination, helper.call)
stop_event.set()
if response.IsSuccessful and total > 0 and progress_callback:
progress_callback(name, 100)

if not response.IsSuccessful:
return None, response.ErrorData or "\n".join(helper.messages)

Expand Down