1388 lines
48 KiB
Python
1388 lines
48 KiB
Python
"""设置页事件、ADB 搜索、Client 保存登记和软件更新。
|
||
|
||
本文件不直接写 SQL。ADB 搜索、SQLite 写入和 Admin HTTP 请求由 Worker 在线程中
|
||
执行;页面启动时只同步读取少量索引设置,后台结果通过信号返回主线程更新页面。
|
||
"""
|
||
|
||
from typing import Optional
|
||
|
||
from PyQt5.QtCore import (
|
||
QCoreApplication,
|
||
QObject,
|
||
QThread,
|
||
QTimer,
|
||
pyqtSignal,
|
||
pyqtSlot,
|
||
)
|
||
|
||
from .adb_runtime import BundledAdbError, validate_bundled_adb
|
||
from .android_device_service import (
|
||
AndroidDeviceConversionCancelled,
|
||
AndroidDeviceSearchError,
|
||
AndroidDeviceSearchResult,
|
||
AndroidDeviceService,
|
||
PDD_PACKAGE_NAME,
|
||
)
|
||
from .admin_gateway import (
|
||
AdminGatewayError,
|
||
AndroidDeviceInfo,
|
||
ClaimCapabilities,
|
||
ClientInfo,
|
||
ClientRegistrationGateway,
|
||
)
|
||
from .current_client_service import (
|
||
CurrentClientService,
|
||
generate_client_device_id,
|
||
load_admin_base_url,
|
||
)
|
||
from .http_admin_gateway import HttpAdminGateway
|
||
from .selected_android_device_service import SelectedAndroidDeviceService
|
||
from .settings_repository import SettingsRepository
|
||
from .settings_ui import AndroidDeviceRow
|
||
from .task_models import TaskType
|
||
from .update_ui_event import UpdateUiEventBinder
|
||
|
||
DEVICE_ID_PLACEHOLDER = "待生成"
|
||
|
||
|
||
class CurrentClientSaveWorker(QObject):
|
||
"""在线程中先保存本地身份,再登记到 Admin。"""
|
||
|
||
localSaved = pyqtSignal(str, str, str)
|
||
localSaveFailed = pyqtSignal(str)
|
||
registrationSucceeded = pyqtSignal(str)
|
||
registrationFailed = pyqtSignal(str)
|
||
completed = pyqtSignal()
|
||
|
||
def __init__(
|
||
self,
|
||
service: CurrentClientService,
|
||
gateway: Optional[ClientRegistrationGateway],
|
||
client_name: str,
|
||
capabilities: ClaimCapabilities,
|
||
admin_base_url: str,
|
||
gateway_error: str = "",
|
||
):
|
||
super().__init__()
|
||
self._service = service
|
||
self._gateway = gateway
|
||
self._client_name = client_name
|
||
self._capabilities = capabilities
|
||
self._admin_base_url = admin_base_url
|
||
self._gateway_error = gateway_error
|
||
self._cancelled = False
|
||
|
||
def cancel(self) -> None:
|
||
"""阻止尚未开始的远端登记;已经发出的 HTTP 等超时返回。"""
|
||
|
||
self._cancelled = True
|
||
|
||
@pyqtSlot()
|
||
def run(self) -> None:
|
||
try:
|
||
try:
|
||
saved = self._service.save(
|
||
self._client_name,
|
||
self._admin_base_url,
|
||
)
|
||
except Exception as exc:
|
||
self.localSaveFailed.emit(str(exc) or "无法写入本地数据库")
|
||
return
|
||
|
||
self.localSaved.emit(
|
||
saved.client_id,
|
||
saved.client_name,
|
||
self._admin_base_url,
|
||
)
|
||
if self._cancelled:
|
||
return
|
||
|
||
if self._gateway is None:
|
||
self.registrationFailed.emit(
|
||
self._gateway_error or "Admin Gateway 尚未配置"
|
||
)
|
||
return
|
||
|
||
try:
|
||
receipt = self._gateway.register_client(
|
||
ClientInfo(saved.client_id, saved.client_name),
|
||
self._capabilities,
|
||
)
|
||
except AdminGatewayError as exc:
|
||
self.registrationFailed.emit(str(exc))
|
||
except Exception as exc:
|
||
self.registrationFailed.emit(str(exc) or "Admin 登记失败")
|
||
else:
|
||
self.registrationSucceeded.emit(receipt.registered_at)
|
||
finally:
|
||
self.completed.emit()
|
||
|
||
|
||
class AndroidDeviceSearchWorker(QObject):
|
||
"""在后台搜索设备,也可先恢复一台已保存设备。"""
|
||
|
||
succeeded = pyqtSignal(object)
|
||
failed = pyqtSignal(str)
|
||
completed = pyqtSignal()
|
||
|
||
def __init__(
|
||
self,
|
||
service: AndroidDeviceService,
|
||
restore_serial: str = "",
|
||
):
|
||
super().__init__()
|
||
self._service = service
|
||
self._restore_serial = restore_serial
|
||
self._cancelled = False
|
||
|
||
def cancel(self) -> None:
|
||
"""停止后续属性查询;正在执行的命令等待自身超时结束。"""
|
||
|
||
self._cancelled = True
|
||
|
||
@pyqtSlot()
|
||
def run(self) -> None:
|
||
try:
|
||
try:
|
||
restore_warning = ""
|
||
if self._restore_serial:
|
||
try:
|
||
devices = self._service.restore_saved_device(
|
||
self._restore_serial,
|
||
lambda: self._cancelled,
|
||
)
|
||
except AndroidDeviceSearchError as exc:
|
||
restore_warning = str(exc)
|
||
devices = self._service.search(
|
||
lambda: self._cancelled
|
||
)
|
||
else:
|
||
devices = self._service.search(lambda: self._cancelled)
|
||
except AndroidDeviceSearchError as exc:
|
||
if not self._cancelled:
|
||
self.failed.emit(str(exc))
|
||
except Exception as exc:
|
||
if not self._cancelled:
|
||
self.failed.emit(str(exc) or "无法搜索 Android 设备")
|
||
else:
|
||
if not self._cancelled:
|
||
self.succeeded.emit(
|
||
AndroidDeviceSearchResult(devices, restore_warning)
|
||
)
|
||
finally:
|
||
self.completed.emit()
|
||
|
||
|
||
class PddPackageCheckWorker(QObject):
|
||
"""在后台检查一台 Android 设备是否安装 PDD。"""
|
||
|
||
succeeded = pyqtSignal(str, bool)
|
||
failed = pyqtSignal(str, str)
|
||
completed = pyqtSignal(str)
|
||
|
||
def __init__(self, service: AndroidDeviceService, serial: str):
|
||
super().__init__()
|
||
self._service = service
|
||
self._serial = serial
|
||
self._cancelled = False
|
||
|
||
def cancel(self) -> None:
|
||
"""ADB 命令无法中断;完成后不再发送业务结果。"""
|
||
|
||
self._cancelled = True
|
||
|
||
@pyqtSlot()
|
||
def run(self) -> None:
|
||
try:
|
||
try:
|
||
installed = self._service.is_package_installed(
|
||
self._serial,
|
||
PDD_PACKAGE_NAME,
|
||
)
|
||
except AndroidDeviceSearchError as exc:
|
||
if not self._cancelled:
|
||
self.failed.emit(self._serial, str(exc))
|
||
except Exception as exc:
|
||
if not self._cancelled:
|
||
self.failed.emit(
|
||
self._serial,
|
||
str(exc) or "无法检测 PDD 应用",
|
||
)
|
||
else:
|
||
if not self._cancelled:
|
||
self.succeeded.emit(self._serial, installed)
|
||
finally:
|
||
self.completed.emit(self._serial)
|
||
|
||
|
||
class AndroidWifiConversionWorker(QObject):
|
||
"""在后台引导一台 USB 设备切换到 Wi-Fi ADB。"""
|
||
|
||
progressChanged = pyqtSignal(str)
|
||
succeeded = pyqtSignal(object)
|
||
failed = pyqtSignal(str)
|
||
completed = pyqtSignal()
|
||
|
||
def __init__(self, service: AndroidDeviceService, usb_serial: str):
|
||
super().__init__()
|
||
self._service = service
|
||
self._usb_serial = usb_serial
|
||
self._cancelled = False
|
||
|
||
def cancel(self) -> None:
|
||
"""停止轮询和后续命令;正在执行的 ADB 命令等待自身超时。"""
|
||
|
||
self._cancelled = True
|
||
|
||
@pyqtSlot()
|
||
def run(self) -> None:
|
||
try:
|
||
try:
|
||
result = self._service.convert_usb_to_wifi(
|
||
self._usb_serial,
|
||
lambda: self._cancelled,
|
||
self.progressChanged.emit,
|
||
)
|
||
except AndroidDeviceConversionCancelled:
|
||
return
|
||
except AndroidDeviceSearchError as exc:
|
||
if not self._cancelled:
|
||
self.failed.emit(str(exc))
|
||
except Exception as exc:
|
||
if not self._cancelled:
|
||
self.failed.emit(str(exc) or "无法将 USB 设备转为 Wi-Fi")
|
||
else:
|
||
if not self._cancelled:
|
||
self.succeeded.emit(result)
|
||
finally:
|
||
self.completed.emit()
|
||
|
||
|
||
class AndroidDeviceSettingWorker(QObject):
|
||
"""在后台保存或删除当前使用的 Android 设备号。"""
|
||
|
||
succeeded = pyqtSignal(str, str)
|
||
failed = pyqtSignal(str, str)
|
||
completed = pyqtSignal()
|
||
|
||
def __init__(
|
||
self,
|
||
service: SelectedAndroidDeviceService,
|
||
action: str,
|
||
serial: str = "",
|
||
):
|
||
super().__init__()
|
||
self._service = service
|
||
self._action = action
|
||
self._serial = serial
|
||
self._cancelled = False
|
||
|
||
def cancel(self) -> None:
|
||
"""写入无法中断;完成后不再向已关闭页面返回结果。"""
|
||
|
||
self._cancelled = True
|
||
|
||
@pyqtSlot()
|
||
def run(self) -> None:
|
||
try:
|
||
try:
|
||
if self._action == "save":
|
||
serial = self._service.save(self._serial)
|
||
elif self._action == "delete":
|
||
self._service.delete()
|
||
serial = ""
|
||
else:
|
||
raise ValueError("未知的 Android 设备设置操作")
|
||
except Exception as exc:
|
||
if not self._cancelled:
|
||
self.failed.emit(
|
||
self._action,
|
||
str(exc) or "无法写入本地数据库",
|
||
)
|
||
else:
|
||
if not self._cancelled:
|
||
self.succeeded.emit(self._action, serial)
|
||
finally:
|
||
self.completed.emit()
|
||
|
||
|
||
class SettingsPageEventBinder(QObject):
|
||
"""绑定设备管理控件,并向应用层发出稳定事件。"""
|
||
|
||
searchRequested = pyqtSignal()
|
||
convertWifiRequested = pyqtSignal(str)
|
||
currentDeviceSaveRequested = pyqtSignal(str, str, str)
|
||
saveRequested = pyqtSignal(str, str)
|
||
deleteRequested = pyqtSignal(str)
|
||
androidDeviceConfigurationChanged = pyqtSignal(str)
|
||
adminBaseUrlChanged = pyqtSignal(str)
|
||
|
||
def __init__(
|
||
self,
|
||
page,
|
||
settings_repository: Optional[SettingsRepository] = None,
|
||
admin_gateway: Optional[ClientRegistrationGateway] = None,
|
||
android_device_service: Optional[AndroidDeviceService] = None,
|
||
update_service=None,
|
||
update_credential_store=None,
|
||
live_purchase_adapter_ready: bool = False,
|
||
):
|
||
super().__init__(page)
|
||
self._page = page
|
||
self._busy = False
|
||
self._current_device_busy = False
|
||
self._closing = False
|
||
self._thread: Optional[QThread] = None
|
||
self._worker: Optional[CurrentClientSaveWorker] = None
|
||
self._search_busy = False
|
||
self._search_restore_serial = ""
|
||
self._search_is_startup_restore = False
|
||
self._search_thread: Optional[QThread] = None
|
||
self._search_worker: Optional[AndroidDeviceSearchWorker] = None
|
||
self._pdd_check_busy = False
|
||
self._pdd_check_serial = ""
|
||
self._pending_pdd_check_serial = ""
|
||
self._pending_android_save_serial = ""
|
||
self._pdd_check_result_serial = ""
|
||
self._pdd_is_installed = False
|
||
self._pdd_check_thread: Optional[QThread] = None
|
||
self._pdd_check_worker: Optional[PddPackageCheckWorker] = None
|
||
self._wifi_conversion_busy = False
|
||
self._wifi_conversion_thread: Optional[QThread] = None
|
||
self._wifi_conversion_worker: Optional[
|
||
AndroidWifiConversionWorker
|
||
] = None
|
||
self._android_setting_busy = False
|
||
self._android_setting_thread: Optional[QThread] = None
|
||
self._android_setting_worker: Optional[AndroidDeviceSettingWorker] = None
|
||
self._saved_android_serial = ""
|
||
self._refresh_registration_after_android_setting = False
|
||
self._live_purchase_adapter_ready = bool(
|
||
live_purchase_adapter_ready
|
||
)
|
||
self._adb_unavailable_message = ""
|
||
if android_device_service is None:
|
||
try:
|
||
validate_bundled_adb()
|
||
except BundledAdbError as exc:
|
||
self._adb_unavailable_message = str(exc)
|
||
self._android_device_service = (
|
||
android_device_service or AndroidDeviceService()
|
||
)
|
||
|
||
repository = settings_repository or SettingsRepository()
|
||
self._settings_repository = repository
|
||
self.updateEventBinder = UpdateUiEventBinder(
|
||
page,
|
||
repository,
|
||
service=update_service,
|
||
credential_store=update_credential_store,
|
||
parent=self,
|
||
)
|
||
self._client_service = CurrentClientService(repository)
|
||
self._selected_android_device_service = SelectedAndroidDeviceService(
|
||
repository
|
||
)
|
||
self._admin_gateway = admin_gateway
|
||
self._owns_admin_gateway = admin_gateway is None
|
||
self._pending_admin_gateway = None
|
||
self._gateway_error = ""
|
||
if self._admin_gateway is None:
|
||
base_url = load_admin_base_url(repository)
|
||
timeout_value = repository.get("admin.request_timeout_seconds", 3.0)
|
||
try:
|
||
timeout_seconds = float(timeout_value)
|
||
self._admin_gateway = HttpAdminGateway(
|
||
base_url if isinstance(base_url, str) else "",
|
||
timeout_seconds=timeout_seconds,
|
||
)
|
||
except (TypeError, ValueError) as exc:
|
||
self._gateway_error = str(exc)
|
||
|
||
page.currentDeviceSaveButton.clicked.connect(
|
||
self._request_save_current_device
|
||
)
|
||
self.currentDeviceSaveRequested.connect(
|
||
self._start_save_current_device
|
||
)
|
||
page.searchButton.clicked.connect(self._request_search)
|
||
self.searchRequested.connect(self._start_search)
|
||
page.convertWifiButton.clicked.connect(self._request_convert_wifi)
|
||
self.convertWifiRequested.connect(self._start_convert_wifi)
|
||
page.saveButton.clicked.connect(self._request_save)
|
||
self.saveRequested.connect(self._start_save_android_device)
|
||
page.deleteButton.clicked.connect(self._request_delete)
|
||
self.deleteRequested.connect(self._start_delete_android_device)
|
||
page.deviceTableModel.checkedDeviceChanged.connect(
|
||
self._on_checked_device_changed
|
||
)
|
||
page.destroyed.connect(self.shutdown)
|
||
application = QCoreApplication.instance()
|
||
if application is not None:
|
||
application.aboutToQuit.connect(self.shutdown)
|
||
|
||
self._load_current_client()
|
||
self._load_selected_android_device()
|
||
if self._adb_unavailable_message:
|
||
self._page.deviceStatusLabel.setText(
|
||
self._adb_unavailable_message
|
||
)
|
||
self._sync_button_state()
|
||
if self._saved_android_serial and not self._adb_unavailable_message:
|
||
QTimer.singleShot(0, self._request_restore_saved_android_device)
|
||
|
||
@pyqtSlot()
|
||
def _request_save_current_device(self) -> None:
|
||
if (
|
||
self._busy
|
||
or self._current_device_busy
|
||
or self._search_busy
|
||
or self._wifi_conversion_busy
|
||
or self._android_setting_busy
|
||
):
|
||
return
|
||
|
||
device_id = self._page.deviceIdInput.text().strip()
|
||
device_name = self._page.deviceNameInput.text().strip()
|
||
admin_base_url = self._page.adminBaseUrlInput.text().strip()
|
||
self._page.deviceNameInput.setText(device_name)
|
||
self.currentDeviceSaveRequested.emit(
|
||
device_id,
|
||
device_name,
|
||
admin_base_url,
|
||
)
|
||
|
||
@pyqtSlot(str, str, str)
|
||
def _start_save_current_device(
|
||
self,
|
||
_displayed_device_id: str,
|
||
device_name: str,
|
||
admin_base_url: str,
|
||
) -> None:
|
||
if (
|
||
self._closing
|
||
or self._current_device_busy
|
||
or self._search_busy
|
||
or self._wifi_conversion_busy
|
||
or self._android_setting_busy
|
||
):
|
||
return
|
||
|
||
try:
|
||
timeout_value = self._settings_repository.get(
|
||
"admin.request_timeout_seconds", 3.0
|
||
)
|
||
timeout_seconds = float(timeout_value)
|
||
validated_gateway = HttpAdminGateway(
|
||
admin_base_url,
|
||
timeout_seconds=timeout_seconds,
|
||
)
|
||
normalized_base_url = admin_base_url.strip().rstrip("/")
|
||
device = self._selected_android_device()
|
||
purchase_mode = (
|
||
"live"
|
||
if device is not None and self._live_purchase_adapter_ready
|
||
else "dry_run"
|
||
)
|
||
supported_types = (
|
||
(TaskType.COLLECT, TaskType.PURCHASE)
|
||
if device is not None and self._live_purchase_adapter_ready
|
||
else (TaskType.COLLECT,)
|
||
)
|
||
capabilities = ClaimCapabilities(
|
||
device=device,
|
||
supported_types=supported_types,
|
||
purchase_mode=purchase_mode,
|
||
)
|
||
except (TypeError, ValueError) as exc:
|
||
self._page.set_current_device_status(f"保存失败:{exc}")
|
||
self._page.adminBaseUrlInput.setFocus()
|
||
return
|
||
|
||
pending_gateway = (
|
||
validated_gateway
|
||
if self._owns_admin_gateway
|
||
else self._admin_gateway
|
||
)
|
||
self._pending_admin_gateway = pending_gateway
|
||
self._page.set_admin_base_url(normalized_base_url)
|
||
|
||
self._current_device_busy = True
|
||
self._sync_button_state()
|
||
self._page.set_current_device_status("正在保存本地设备信息…")
|
||
|
||
thread = QThread(self)
|
||
worker = CurrentClientSaveWorker(
|
||
self._client_service,
|
||
pending_gateway,
|
||
device_name,
|
||
capabilities,
|
||
normalized_base_url,
|
||
"",
|
||
)
|
||
worker.moveToThread(thread)
|
||
|
||
thread.started.connect(worker.run)
|
||
worker.localSaved.connect(self._on_local_saved)
|
||
worker.localSaveFailed.connect(self._on_local_save_failed)
|
||
worker.registrationSucceeded.connect(self._on_registration_succeeded)
|
||
worker.registrationFailed.connect(self._on_registration_failed)
|
||
worker.completed.connect(thread.quit)
|
||
worker.completed.connect(worker.deleteLater)
|
||
thread.finished.connect(self._on_save_thread_finished)
|
||
thread.finished.connect(thread.deleteLater)
|
||
|
||
self._thread = thread
|
||
self._worker = worker
|
||
thread.start()
|
||
|
||
@pyqtSlot()
|
||
def _request_search(self) -> None:
|
||
if (
|
||
self._busy
|
||
or self._current_device_busy
|
||
or self._search_busy
|
||
or self._wifi_conversion_busy
|
||
or self._android_setting_busy
|
||
):
|
||
return
|
||
self.searchRequested.emit()
|
||
|
||
@pyqtSlot()
|
||
def _start_search(self) -> None:
|
||
restore_serial = (
|
||
self._saved_android_serial
|
||
if ":" in self._saved_android_serial
|
||
else ""
|
||
)
|
||
self._start_search_worker(restore_serial)
|
||
|
||
@pyqtSlot()
|
||
def _request_restore_saved_android_device(self) -> None:
|
||
if not self._saved_android_serial:
|
||
return
|
||
self._start_search_worker(
|
||
self._saved_android_serial,
|
||
is_startup_restore=True,
|
||
)
|
||
|
||
def _start_search_worker(
|
||
self,
|
||
restore_serial: str = "",
|
||
is_startup_restore: bool = False,
|
||
) -> None:
|
||
if (
|
||
self._closing
|
||
or self._busy
|
||
or self._current_device_busy
|
||
or self._search_busy
|
||
or self._wifi_conversion_busy
|
||
or self._android_setting_busy
|
||
):
|
||
return
|
||
|
||
self._search_busy = True
|
||
self._search_restore_serial = restore_serial
|
||
self._search_is_startup_restore = is_startup_restore
|
||
self._sync_button_state()
|
||
if is_startup_restore:
|
||
status = f"正在恢复已保存设备:{restore_serial}…"
|
||
elif restore_serial:
|
||
status = f"正在重新连接已保存 Wi-Fi 设备:{restore_serial}…"
|
||
else:
|
||
status = "正在搜索 Android 设备…"
|
||
self._page.deviceStatusLabel.setText(status)
|
||
|
||
thread = QThread(self)
|
||
worker = AndroidDeviceSearchWorker(
|
||
self._android_device_service,
|
||
restore_serial,
|
||
)
|
||
worker.moveToThread(thread)
|
||
|
||
thread.started.connect(worker.run)
|
||
worker.succeeded.connect(self._on_search_succeeded)
|
||
worker.failed.connect(self._on_search_failed)
|
||
worker.completed.connect(thread.quit)
|
||
worker.completed.connect(worker.deleteLater)
|
||
thread.finished.connect(self._on_search_thread_finished)
|
||
thread.finished.connect(thread.deleteLater)
|
||
|
||
self._search_thread = thread
|
||
self._search_worker = worker
|
||
thread.start()
|
||
|
||
@pyqtSlot(str)
|
||
def _on_checked_device_changed(self, serial: str) -> None:
|
||
"""勾选变化后刷新按钮,并为最新设备排队检测 PDD。"""
|
||
|
||
self._sync_button_state()
|
||
if self._closing:
|
||
return
|
||
|
||
if self._pending_android_save_serial != serial:
|
||
self._pending_android_save_serial = ""
|
||
self._pdd_check_result_serial = ""
|
||
self._pdd_is_installed = False
|
||
|
||
if not serial:
|
||
self._pending_pdd_check_serial = ""
|
||
self._page.set_pdd_app_status("未检测")
|
||
return
|
||
self._queue_pdd_package_check(serial)
|
||
|
||
def _queue_pdd_package_check(self, serial: str) -> None:
|
||
"""立即检测或只保留最后一次待检测的设备号。"""
|
||
|
||
if self._closing or not serial:
|
||
return
|
||
if self._pdd_check_busy:
|
||
if serial != self._pdd_check_serial:
|
||
self._pending_pdd_check_serial = serial
|
||
self._page.set_pdd_app_status("等待检测…")
|
||
return
|
||
if (
|
||
self._busy
|
||
or self._current_device_busy
|
||
or self._search_busy
|
||
or self._wifi_conversion_busy
|
||
or self._android_setting_busy
|
||
):
|
||
self._pending_pdd_check_serial = serial
|
||
self._page.set_pdd_app_status("等待检测…")
|
||
return
|
||
self._start_pdd_package_check(serial)
|
||
|
||
def _start_pdd_package_check(self, serial: str) -> None:
|
||
"""启动单次后台包检测;同一时间最多运行一个。"""
|
||
|
||
if self._closing or self._pdd_check_busy:
|
||
return
|
||
|
||
self._pending_pdd_check_serial = ""
|
||
self._pdd_check_busy = True
|
||
self._pdd_check_serial = serial
|
||
self._page.set_pdd_app_status("正在检测…")
|
||
self._sync_button_state()
|
||
|
||
thread = QThread(self)
|
||
worker = PddPackageCheckWorker(
|
||
self._android_device_service,
|
||
serial,
|
||
)
|
||
worker.moveToThread(thread)
|
||
|
||
thread.started.connect(worker.run)
|
||
worker.succeeded.connect(self._on_pdd_package_check_succeeded)
|
||
worker.failed.connect(self._on_pdd_package_check_failed)
|
||
worker.completed.connect(thread.quit)
|
||
worker.completed.connect(worker.deleteLater)
|
||
thread.finished.connect(self._on_pdd_package_check_finished)
|
||
thread.finished.connect(thread.deleteLater)
|
||
|
||
self._pdd_check_thread = thread
|
||
self._pdd_check_worker = worker
|
||
thread.start()
|
||
|
||
@pyqtSlot(str, bool)
|
||
def _on_pdd_package_check_succeeded(
|
||
self,
|
||
serial: str,
|
||
installed: bool,
|
||
) -> None:
|
||
if self._closing:
|
||
return
|
||
if serial != self._page.deviceTableModel.checked_serial:
|
||
return
|
||
|
||
self._pdd_check_result_serial = serial
|
||
self._pdd_is_installed = installed
|
||
if installed:
|
||
self._page.set_pdd_app_status(
|
||
f"{PDD_PACKAGE_NAME}(已安装)"
|
||
)
|
||
return
|
||
|
||
self._page.set_pdd_app_status("未安装,请先在手机安装拼多多")
|
||
if self._pending_android_save_serial == serial:
|
||
self._pending_android_save_serial = ""
|
||
self._page.deviceStatusLabel.setText(
|
||
"保存失败:所选设备未安装 PDD,请安装后重试"
|
||
)
|
||
|
||
@pyqtSlot(str, str)
|
||
def _on_pdd_package_check_failed(
|
||
self,
|
||
serial: str,
|
||
message: str,
|
||
) -> None:
|
||
if self._closing:
|
||
return
|
||
if serial != self._page.deviceTableModel.checked_serial:
|
||
return
|
||
|
||
self._pdd_check_result_serial = serial
|
||
self._pdd_is_installed = False
|
||
self._page.set_pdd_app_status(f"检测失败:{message}")
|
||
if self._pending_android_save_serial == serial:
|
||
self._pending_android_save_serial = ""
|
||
self._page.deviceStatusLabel.setText(
|
||
f"保存失败:无法确认 PDD 安装状态;{message};原配置未更改"
|
||
)
|
||
|
||
@pyqtSlot()
|
||
def _on_pdd_package_check_finished(self) -> None:
|
||
finished_serial = self._pdd_check_serial
|
||
self._pdd_check_worker = None
|
||
self._pdd_check_thread = None
|
||
self._pdd_check_busy = False
|
||
self._pdd_check_serial = ""
|
||
if self._closing:
|
||
return
|
||
|
||
current_serial = self._page.deviceTableModel.checked_serial
|
||
pending_serial = self._pending_pdd_check_serial
|
||
if pending_serial and pending_serial == current_serial:
|
||
self._queue_pdd_package_check(pending_serial)
|
||
return
|
||
self._pending_pdd_check_serial = ""
|
||
|
||
if (
|
||
self._pending_android_save_serial == finished_serial
|
||
and self._pdd_check_result_serial == finished_serial
|
||
and self._pdd_is_installed
|
||
and current_serial == finished_serial
|
||
):
|
||
self._pending_android_save_serial = ""
|
||
self.saveRequested.emit("", finished_serial)
|
||
return
|
||
self._sync_button_state()
|
||
|
||
@pyqtSlot()
|
||
def _request_convert_wifi(self) -> None:
|
||
if (
|
||
self._busy
|
||
or self._current_device_busy
|
||
or self._search_busy
|
||
or self._wifi_conversion_busy
|
||
or self._android_setting_busy
|
||
):
|
||
return
|
||
|
||
serial = self._page.deviceTableModel.checked_serial
|
||
device = self._page.deviceTableModel.device_for_serial(serial)
|
||
if device is None or device.status != "device":
|
||
self._page.deviceStatusLabel.setText(
|
||
"转换失败:请先勾选一台已连接的 USB 设备"
|
||
)
|
||
self._page.deviceTable.setFocus()
|
||
return
|
||
if device.connection_type != "USB":
|
||
self._page.deviceStatusLabel.setText(
|
||
"转换失败:当前设备已经是 Wi-Fi 连接"
|
||
)
|
||
return
|
||
self.convertWifiRequested.emit(serial)
|
||
|
||
@pyqtSlot(str)
|
||
def _start_convert_wifi(self, usb_serial: str) -> None:
|
||
if (
|
||
self._closing
|
||
or self._busy
|
||
or self._current_device_busy
|
||
or self._search_busy
|
||
or self._wifi_conversion_busy
|
||
or self._android_setting_busy
|
||
):
|
||
return
|
||
|
||
self._wifi_conversion_busy = True
|
||
self._sync_button_state()
|
||
self._page.deviceStatusLabel.setText("正在准备 USB 转 Wi-Fi…")
|
||
|
||
thread = QThread(self)
|
||
worker = AndroidWifiConversionWorker(
|
||
self._android_device_service,
|
||
usb_serial,
|
||
)
|
||
worker.moveToThread(thread)
|
||
|
||
thread.started.connect(worker.run)
|
||
worker.progressChanged.connect(self._on_wifi_conversion_progress)
|
||
worker.succeeded.connect(self._on_wifi_conversion_succeeded)
|
||
worker.failed.connect(self._on_wifi_conversion_failed)
|
||
worker.completed.connect(thread.quit)
|
||
worker.completed.connect(worker.deleteLater)
|
||
thread.finished.connect(self._on_wifi_conversion_finished)
|
||
thread.finished.connect(thread.deleteLater)
|
||
|
||
self._wifi_conversion_thread = thread
|
||
self._wifi_conversion_worker = worker
|
||
thread.start()
|
||
|
||
@pyqtSlot()
|
||
def _request_save(self) -> None:
|
||
if (
|
||
self._busy
|
||
or self._search_busy
|
||
or self._wifi_conversion_busy
|
||
or self._android_setting_busy
|
||
):
|
||
return
|
||
|
||
serial = self._page.deviceTableModel.checked_serial
|
||
if not serial:
|
||
self._page.deviceStatusLabel.setText("保存失败:请先勾选一台已连接设备")
|
||
self._page.deviceTable.setFocus()
|
||
return
|
||
|
||
self._pending_android_save_serial = serial
|
||
self._page.deviceStatusLabel.setText(
|
||
"正在确认所选设备是否安装 PDD…"
|
||
)
|
||
self._queue_pdd_package_check(serial)
|
||
|
||
@pyqtSlot(str, str)
|
||
def _start_save_android_device(
|
||
self, _unused_device_name: str, serial: str
|
||
) -> None:
|
||
self._start_android_device_setting("save", serial)
|
||
|
||
@pyqtSlot()
|
||
def _request_delete(self) -> None:
|
||
if (
|
||
self._busy
|
||
or self._search_busy
|
||
or self._wifi_conversion_busy
|
||
or self._android_setting_busy
|
||
):
|
||
return
|
||
|
||
serial = self._saved_android_serial
|
||
if not serial:
|
||
self._page.deviceStatusLabel.setText("删除失败:当前没有已保存设备")
|
||
return
|
||
self.deleteRequested.emit(serial)
|
||
|
||
@pyqtSlot(str)
|
||
def _start_delete_android_device(self, _serial: str) -> None:
|
||
self._start_android_device_setting("delete")
|
||
|
||
def _start_android_device_setting(
|
||
self, action: str, serial: str = ""
|
||
) -> None:
|
||
if (
|
||
self._closing
|
||
or self._busy
|
||
or self._search_busy
|
||
or self._wifi_conversion_busy
|
||
or self._android_setting_busy
|
||
):
|
||
return
|
||
|
||
self._android_setting_busy = True
|
||
self._sync_button_state()
|
||
message = (
|
||
"正在保存当前使用设备…"
|
||
if action == "save"
|
||
else "正在删除已保存设备配置…"
|
||
)
|
||
self._page.deviceStatusLabel.setText(message)
|
||
|
||
thread = QThread(self)
|
||
worker = AndroidDeviceSettingWorker(
|
||
self._selected_android_device_service,
|
||
action,
|
||
serial,
|
||
)
|
||
worker.moveToThread(thread)
|
||
|
||
thread.started.connect(worker.run)
|
||
worker.succeeded.connect(self._on_android_device_setting_succeeded)
|
||
worker.failed.connect(self._on_android_device_setting_failed)
|
||
worker.completed.connect(thread.quit)
|
||
worker.completed.connect(worker.deleteLater)
|
||
thread.finished.connect(self._on_android_device_setting_finished)
|
||
thread.finished.connect(thread.deleteLater)
|
||
|
||
self._android_setting_thread = thread
|
||
self._android_setting_worker = worker
|
||
thread.start()
|
||
|
||
@pyqtSlot()
|
||
def _sync_button_state(self, *_args) -> None:
|
||
serial = self._page.deviceTableModel.checked_serial
|
||
selected_device = self._page.deviceTableModel.device_for_serial(serial)
|
||
self._page.currentDeviceSaveButton.setEnabled(
|
||
not self._busy
|
||
and not self._current_device_busy
|
||
and not self._search_busy
|
||
and not self._wifi_conversion_busy
|
||
and not self._android_setting_busy
|
||
)
|
||
device_commands_enabled = (
|
||
not self._adb_unavailable_message
|
||
and not self._busy
|
||
and not self._search_busy
|
||
and not self._wifi_conversion_busy
|
||
and not self._android_setting_busy
|
||
)
|
||
self._page.searchButton.setEnabled(
|
||
device_commands_enabled and not self._current_device_busy
|
||
)
|
||
self._page.saveButton.setEnabled(
|
||
device_commands_enabled
|
||
and not self._pdd_check_busy
|
||
and bool(serial)
|
||
)
|
||
self._page.convertWifiButton.setEnabled(
|
||
device_commands_enabled
|
||
and not self._current_device_busy
|
||
and selected_device is not None
|
||
and selected_device.status == "device"
|
||
and selected_device.connection_type == "USB"
|
||
)
|
||
self._page.deleteButton.setEnabled(
|
||
device_commands_enabled and bool(self._saved_android_serial)
|
||
)
|
||
|
||
def set_busy(self, busy: bool, message: str = "") -> None:
|
||
"""切换界面忙碌状态,防止用户重复提交设备命令。"""
|
||
|
||
self._busy = busy
|
||
self._sync_button_state()
|
||
if message:
|
||
self._page.deviceStatusLabel.setText(message)
|
||
|
||
def _load_current_client(self) -> None:
|
||
"""页面创建时恢复本地 Client 信息。"""
|
||
|
||
try:
|
||
displayed_base_url = load_admin_base_url(
|
||
self._settings_repository
|
||
)
|
||
saved = self._client_service.load()
|
||
except Exception as exc:
|
||
self._page.set_current_device_status(
|
||
f"读取本地设备信息失败:{str(exc) or '数据库不可用'}"
|
||
)
|
||
return
|
||
|
||
self._page.set_admin_base_url(displayed_base_url)
|
||
self._page.set_client_info(saved.client_id, saved.client_name)
|
||
if saved.client_id:
|
||
self._page.set_current_device_status("本地设备信息已加载")
|
||
|
||
def _load_selected_android_device(self) -> None:
|
||
"""页面创建时恢复自动化应使用的 Android 设备号。"""
|
||
|
||
try:
|
||
serial = self._selected_android_device_service.load()
|
||
except Exception as exc:
|
||
self._page.deviceStatusLabel.setText(
|
||
f"读取已保存 Android 设备失败:{str(exc) or '数据库不可用'}"
|
||
)
|
||
return
|
||
|
||
self._saved_android_serial = serial
|
||
if serial:
|
||
self._page.deviceStatusLabel.setText(
|
||
f"当前使用设备:{serial}(等待搜索确认连接)"
|
||
)
|
||
|
||
def _selected_android_device(self) -> Optional[AndroidDeviceInfo]:
|
||
serial = self._selected_android_device_service.load().strip()
|
||
return AndroidDeviceInfo(serial) if serial else None
|
||
|
||
@pyqtSlot(object)
|
||
def _on_search_succeeded(self, result: AndroidDeviceSearchResult) -> None:
|
||
if self._closing:
|
||
return
|
||
|
||
rows = [
|
||
AndroidDeviceRow(
|
||
serial=device.serial,
|
||
connection_type=device.connection_type,
|
||
model=device.model,
|
||
android_version=device.android_version,
|
||
status=device.status,
|
||
)
|
||
for device in result.devices
|
||
]
|
||
self._page.set_android_devices(rows)
|
||
saved_device = self._page.deviceTableModel.device_for_serial(
|
||
self._saved_android_serial
|
||
)
|
||
if saved_device is not None and saved_device.status == "device":
|
||
self._page.deviceTableModel.set_checked_serial(
|
||
self._saved_android_serial
|
||
)
|
||
|
||
if result.restore_warning:
|
||
action = (
|
||
"自动恢复未完成"
|
||
if self._search_is_startup_restore
|
||
else "搜索完成"
|
||
)
|
||
found_text = (
|
||
f"找到 {len(rows)} 台 Android 设备"
|
||
if rows
|
||
else "未找到其他 Android 设备"
|
||
)
|
||
self._page.deviceStatusLabel.setText(
|
||
f"{action}:{found_text};已保存设备 "
|
||
f"{self._saved_android_serial} 恢复失败:"
|
||
f"{result.restore_warning};配置已保留"
|
||
)
|
||
elif saved_device is not None and saved_device.status == "device":
|
||
action = (
|
||
"恢复完成"
|
||
if self._search_is_startup_restore
|
||
else "搜索完成"
|
||
)
|
||
self._page.deviceStatusLabel.setText(
|
||
f"{action}:找到 {len(rows)} 台 Android 设备;"
|
||
"已自动选择当前使用设备"
|
||
)
|
||
elif saved_device is not None and saved_device.status == "unauthorized":
|
||
self._page.deviceStatusLabel.setText(
|
||
"已重新连接 Wi-Fi 设备,但尚未授权:"
|
||
f"{self._saved_android_serial};请在手机上允许调试后重试"
|
||
)
|
||
elif saved_device is not None:
|
||
self._page.deviceStatusLabel.setText(
|
||
"已保存设备当前不可用:"
|
||
f"{self._saved_android_serial}({saved_device.status});"
|
||
"配置已保留"
|
||
)
|
||
elif self._saved_android_serial:
|
||
self._page.deviceStatusLabel.setText(
|
||
"已保存设备当前未连接:"
|
||
f"{self._saved_android_serial};配置已保留"
|
||
)
|
||
elif rows:
|
||
self._page.deviceStatusLabel.setText(
|
||
f"搜索完成:找到 {len(rows)} 台 Android 设备"
|
||
)
|
||
else:
|
||
self._page.deviceStatusLabel.setText(
|
||
"未找到 Android 设备,请检查 USB 调试或无线连接后重试"
|
||
)
|
||
|
||
selected_serial = self._page.deviceTableModel.checked_serial
|
||
if selected_serial:
|
||
self._queue_pdd_package_check(selected_serial)
|
||
else:
|
||
self._page.set_pdd_app_status("未检测")
|
||
|
||
@pyqtSlot(str)
|
||
def _on_search_failed(self, message: str) -> None:
|
||
if self._closing:
|
||
return
|
||
if self._search_is_startup_restore:
|
||
self._page.deviceStatusLabel.setText(
|
||
f"自动恢复失败:{message};已保存配置未更改,"
|
||
"可点击搜索重试"
|
||
)
|
||
elif self._search_restore_serial:
|
||
self._page.deviceStatusLabel.setText(
|
||
f"搜索失败:无法重新连接已保存 Wi-Fi 设备;{message};"
|
||
"已保存配置未更改,可重试"
|
||
)
|
||
else:
|
||
self._page.deviceStatusLabel.setText(
|
||
f"搜索失败:{message};设备列表和已保存配置未更改"
|
||
)
|
||
|
||
@pyqtSlot()
|
||
def _on_search_thread_finished(self) -> None:
|
||
self._search_worker = None
|
||
self._search_thread = None
|
||
self._search_busy = False
|
||
self._search_restore_serial = ""
|
||
self._search_is_startup_restore = False
|
||
if not self._closing:
|
||
self._sync_button_state()
|
||
pending_serial = self._pending_pdd_check_serial
|
||
if pending_serial:
|
||
self._queue_pdd_package_check(pending_serial)
|
||
|
||
@pyqtSlot(str)
|
||
def _on_wifi_conversion_progress(self, message: str) -> None:
|
||
if not self._closing:
|
||
self._page.deviceStatusLabel.setText(message)
|
||
|
||
@pyqtSlot(object)
|
||
def _on_wifi_conversion_succeeded(self, result) -> None:
|
||
if self._closing:
|
||
return
|
||
|
||
rows = [
|
||
AndroidDeviceRow(
|
||
serial=device.serial,
|
||
connection_type=device.connection_type,
|
||
model=device.model,
|
||
android_version=device.android_version,
|
||
status=device.status,
|
||
)
|
||
for device in result.devices
|
||
]
|
||
self._page.set_android_devices(rows)
|
||
self._page.deviceTableModel.set_checked_serial(result.wifi_serial)
|
||
self._page.deviceStatusLabel.setText(
|
||
f"Wi-Fi 设备 {result.wifi_serial} 已连接并勾选;"
|
||
"请确认后点击保存,再拔掉 USB 数据线"
|
||
)
|
||
|
||
@pyqtSlot(str)
|
||
def _on_wifi_conversion_failed(self, message: str) -> None:
|
||
if self._closing:
|
||
return
|
||
self._page.deviceStatusLabel.setText(
|
||
f"转换失败:{message};设备列表和已保存配置未更改,可重试"
|
||
)
|
||
|
||
@pyqtSlot()
|
||
def _on_wifi_conversion_finished(self) -> None:
|
||
self._wifi_conversion_worker = None
|
||
self._wifi_conversion_thread = None
|
||
self._wifi_conversion_busy = False
|
||
if not self._closing:
|
||
self._sync_button_state()
|
||
pending_serial = self._pending_pdd_check_serial
|
||
if pending_serial:
|
||
self._queue_pdd_package_check(pending_serial)
|
||
|
||
@pyqtSlot(str, str)
|
||
def _on_android_device_setting_succeeded(
|
||
self, action: str, serial: str
|
||
) -> None:
|
||
if self._closing:
|
||
return
|
||
|
||
if action == "save":
|
||
self._saved_android_serial = serial
|
||
self._page.deviceStatusLabel.setText(
|
||
f"当前使用设备:{serial}(已保存)"
|
||
)
|
||
self.androidDeviceConfigurationChanged.emit(serial)
|
||
else:
|
||
self._saved_android_serial = ""
|
||
self._page.deviceTableModel.set_checked_serial("")
|
||
self._page.deviceStatusLabel.setText(
|
||
"当前使用设备:未选择(本地配置已删除)"
|
||
)
|
||
self.androidDeviceConfigurationChanged.emit("")
|
||
displayed_client_id = self._page.deviceIdInput.text().strip()
|
||
self._refresh_registration_after_android_setting = bool(
|
||
displayed_client_id and displayed_client_id != DEVICE_ID_PLACEHOLDER
|
||
)
|
||
|
||
@pyqtSlot(str, str)
|
||
def _on_android_device_setting_failed(
|
||
self, action: str, message: str
|
||
) -> None:
|
||
if self._closing:
|
||
return
|
||
action_text = "保存" if action == "save" else "删除"
|
||
self._page.deviceStatusLabel.setText(
|
||
f"{action_text}失败:{message};原设备配置未更改,可重试"
|
||
)
|
||
self._refresh_registration_after_android_setting = False
|
||
|
||
@pyqtSlot()
|
||
def _on_android_device_setting_finished(self) -> None:
|
||
self._android_setting_worker = None
|
||
self._android_setting_thread = None
|
||
self._android_setting_busy = False
|
||
if not self._closing:
|
||
self._sync_button_state()
|
||
if self._refresh_registration_after_android_setting:
|
||
self._refresh_registration_after_android_setting = False
|
||
QTimer.singleShot(0, self._request_save_current_device)
|
||
|
||
@pyqtSlot(str, str, str)
|
||
def _on_local_saved(
|
||
self,
|
||
client_id: str,
|
||
client_name: str,
|
||
admin_base_url: str,
|
||
) -> None:
|
||
if self._closing:
|
||
return
|
||
self._page.set_client_info(client_id, client_name)
|
||
self._page.set_admin_base_url(admin_base_url)
|
||
self._admin_gateway = self._pending_admin_gateway
|
||
self._gateway_error = ""
|
||
self.adminBaseUrlChanged.emit(admin_base_url)
|
||
self._page.set_current_device_status(
|
||
"本地已保存,正在登记到 Admin…"
|
||
)
|
||
|
||
@pyqtSlot(str)
|
||
def _on_local_save_failed(self, message: str) -> None:
|
||
if self._closing:
|
||
return
|
||
self._page.set_current_device_status(f"本地保存失败:{message}")
|
||
self._pending_admin_gateway = None
|
||
|
||
@pyqtSlot(str)
|
||
def _on_registration_succeeded(self, _registered_at: str) -> None:
|
||
if self._closing:
|
||
return
|
||
self._page.set_current_device_status("本地已保存,已登记到 Admin")
|
||
|
||
@pyqtSlot(str)
|
||
def _on_registration_failed(self, message: str) -> None:
|
||
if self._closing:
|
||
return
|
||
self._page.set_current_device_status(
|
||
f"本地已保存,Admin 登记失败:{message};可再次点击保存重试"
|
||
)
|
||
|
||
@pyqtSlot()
|
||
def _on_save_thread_finished(self) -> None:
|
||
self._worker = None
|
||
self._thread = None
|
||
self._current_device_busy = False
|
||
self._pending_admin_gateway = None
|
||
if not self._closing:
|
||
self._sync_button_state()
|
||
|
||
@pyqtSlot()
|
||
def shutdown(self) -> None:
|
||
"""窗口关闭时忽略迟到结果,并等待短请求安全结束。"""
|
||
|
||
if self._closing:
|
||
return
|
||
self._closing = True
|
||
self.updateEventBinder.shutdown()
|
||
|
||
worker = self._worker
|
||
thread = self._thread
|
||
if worker is not None:
|
||
worker.cancel()
|
||
for signal, slot in (
|
||
(worker.localSaved, self._on_local_saved),
|
||
(worker.localSaveFailed, self._on_local_save_failed),
|
||
(worker.registrationSucceeded, self._on_registration_succeeded),
|
||
(worker.registrationFailed, self._on_registration_failed),
|
||
):
|
||
try:
|
||
signal.disconnect(slot)
|
||
except TypeError:
|
||
pass
|
||
|
||
if thread is not None and thread.isRunning():
|
||
thread.quit()
|
||
thread.wait(4000)
|
||
|
||
search_worker = self._search_worker
|
||
search_thread = self._search_thread
|
||
if search_worker is not None:
|
||
try:
|
||
search_worker.cancel()
|
||
signal_slots = (
|
||
(search_worker.succeeded, self._on_search_succeeded),
|
||
(search_worker.failed, self._on_search_failed),
|
||
)
|
||
except RuntimeError:
|
||
# Worker 已调用 deleteLater,但 finished 清理槽还在消息队列中。
|
||
signal_slots = ()
|
||
for signal, slot in signal_slots:
|
||
try:
|
||
signal.disconnect(slot)
|
||
except (TypeError, RuntimeError):
|
||
pass
|
||
|
||
if search_thread is not None and search_thread.isRunning():
|
||
search_thread.quit()
|
||
search_thread.wait(6000)
|
||
|
||
pdd_worker = self._pdd_check_worker
|
||
pdd_thread = self._pdd_check_thread
|
||
if pdd_worker is not None:
|
||
try:
|
||
pdd_worker.cancel()
|
||
signal_slots = (
|
||
(
|
||
pdd_worker.succeeded,
|
||
self._on_pdd_package_check_succeeded,
|
||
),
|
||
(
|
||
pdd_worker.failed,
|
||
self._on_pdd_package_check_failed,
|
||
),
|
||
)
|
||
except RuntimeError:
|
||
# Worker 已调用 deleteLater,但 thread.finished 的清理槽
|
||
# 还在主线程队列中时,Python 引用可能暂时仍存在。
|
||
signal_slots = ()
|
||
for signal, slot in signal_slots:
|
||
try:
|
||
signal.disconnect(slot)
|
||
except (TypeError, RuntimeError):
|
||
pass
|
||
|
||
if pdd_thread is not None and pdd_thread.isRunning():
|
||
pdd_thread.quit()
|
||
pdd_thread.wait(6000)
|
||
|
||
conversion_worker = self._wifi_conversion_worker
|
||
conversion_thread = self._wifi_conversion_thread
|
||
if conversion_worker is not None:
|
||
try:
|
||
conversion_worker.cancel()
|
||
signal_slots = (
|
||
(
|
||
conversion_worker.progressChanged,
|
||
self._on_wifi_conversion_progress,
|
||
),
|
||
(
|
||
conversion_worker.succeeded,
|
||
self._on_wifi_conversion_succeeded,
|
||
),
|
||
(
|
||
conversion_worker.failed,
|
||
self._on_wifi_conversion_failed,
|
||
),
|
||
)
|
||
except RuntimeError:
|
||
signal_slots = ()
|
||
for signal, slot in signal_slots:
|
||
try:
|
||
signal.disconnect(slot)
|
||
except (TypeError, RuntimeError):
|
||
pass
|
||
|
||
if conversion_thread is not None and conversion_thread.isRunning():
|
||
conversion_thread.quit()
|
||
conversion_thread.wait(6000)
|
||
|
||
setting_worker = self._android_setting_worker
|
||
setting_thread = self._android_setting_thread
|
||
if setting_worker is not None:
|
||
try:
|
||
setting_worker.cancel()
|
||
signal_slots = (
|
||
(
|
||
setting_worker.succeeded,
|
||
self._on_android_device_setting_succeeded,
|
||
),
|
||
(
|
||
setting_worker.failed,
|
||
self._on_android_device_setting_failed,
|
||
),
|
||
)
|
||
except RuntimeError:
|
||
signal_slots = ()
|
||
for signal, slot in signal_slots:
|
||
try:
|
||
signal.disconnect(slot)
|
||
except (TypeError, RuntimeError):
|
||
pass
|
||
|
||
if setting_thread is not None and setting_thread.isRunning():
|
||
setting_thread.quit()
|
||
setting_thread.wait(4000)
|