""" Android Print Service 组件 — PrintService, PrintJob, PrinterDiscoverySession, PrinterId 等 方法签名完全对齐 Android SDK (android.printservice 包) """ from __future__ import annotations import os import time from typing import Any, Callable, Dict, List, Optional, Set, Tuple from .._core import Stub, AndroidOnlyError from ..java.lang import RuntimeException # ═══════════════════════════════════════════════════════════════ # PrinterId # ═══════════════════════════════════════════════════════════════ class PrinterId(Stub): """PrinterId — 打印机 ID""" def __init__(self, package_name: str = "", local_id: str = ""): super().__init__() self._package_name = package_name self._local_id = local_id def getPackageName(self) -> str: return self._package_name def setPackageName(self, pkg: str): self._package_name = pkg def getLocalId(self) -> str: return self._local_id def setLocalId(self, local_id: str): self._local_id = local_id def toString(self) -> str: return f"PrinterId(package={self._package_name}, localId={self._local_id})" def equals(self, other: Any) -> bool: if isinstance(other, PrinterId): return (self._package_name == other._package_name and self._local_id == other._local_id) return False def hashCode(self) -> int: return hash((self._package_name, self._local_id)) & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # PrinterInfo # ═══════════════════════════════════════════════════════════════ class PrinterInfo(Stub): """PrinterInfo — 打印机信息""" STATUS_IDLE = 0 STATUS_BUSY = 1 STATUS_UNAVAILABLE = 2 STATUS_DISABLED = 3 STATUS_LOW_ON_PAPER = 4 STATUS_LOW_ON_INK = 5 STATUS_LOW_ON_TONER = 6 STATUS_WAITING = 7 STATUS_PRINTING = 8 STATUS_ERROR = 9 def __init__(self, printer_id: PrinterId = None, name: str = "", status: int = STATUS_IDLE): super().__init__() self._id = printer_id or PrinterId() self._name = name self._status = status self._description: str = "" self._capabilities: Optional[PrinterCapabilities] = None self._icon: Optional[bytes] = None self._is_default: bool = False self._printer_location: str = "" self._printer_model: str = "" self._printer_make_and_model: str = "" def getId(self) -> PrinterId: return self._id def setId(self, printer_id: PrinterId): self._id = printer_id def getName(self) -> str: return self._name def setName(self, name: str): self._name = name def getStatus(self) -> int: return self._status def setStatus(self, status: int): self._status = status def getDescription(self) -> str: return self._description def setDescription(self, desc: str): self._description = desc def getCapabilities(self) -> Optional[PrinterCapabilities]: return self._capabilities def setCapabilities(self, caps: PrinterCapabilities): self._capabilities = caps def getIcon(self) -> Optional[bytes]: return self._icon def setIcon(self, icon: bytes): self._icon = icon def isDefault(self) -> bool: return self._is_default def setDefault(self, is_default: bool): self._is_default = is_default def toString(self) -> str: return f"PrinterInfo(name={self._name}, status={self._status})" # ═══════════════════════════════════════════════════════════════ # PrinterCapabilities # ═══════════════════════════════════════════════════════════════ class PrinterCapabilities(Stub): """PrinterCapabilities — 打印机能力""" def __init__(self): super().__init__() self._color_supported: bool = False self._duplex_supported: bool = False self._media_sizes: List[MediaSize] = [] self._resolutions: List[Resolution] = [] self._min_margins: Margins = Margins(0, 0, 0, 0) self._paper_sizes: List[PaperSize] = [] def isColorSupported(self) -> bool: return self._color_supported def setColorSupported(self, supported: bool): self._color_supported = supported def isDuplexSupported(self) -> bool: return self._duplex_supported def setDuplexSupported(self, supported: bool): self._duplex_supported = supported def getMediaSizes(self) -> List[MediaSize]: return list(self._media_sizes) def setMediaSizes(self, sizes: List[MediaSize]): self._media_sizes = list(sizes) def getResolutions(self) -> List[Resolution]: return list(self._resolutions) def setResolutions(self, resolutions: List[Resolution]): self._resolutions = list(resolutions) def getMinMargins(self) -> Margins: return self._min_margins def setMinMargins(self, margins: Margins): self._min_margins = margins def toString(self) -> str: return f"PrinterCapabilities(color={self._color_supported}, duplex={self._duplex_supported})" # ═══════════════════════════════════════════════════════════════ # MediaSize / Resolution / Margins / PaperSize # ═══════════════════════════════════════════════════════════════ class MediaSize(Stub): """MediaSize — 打印介质尺寸""" def __init__(self, id: str = "", label: str = "", width: int = 0, height: int = 0): super().__init__() self._id = id self._label = label self._width = width self._height = height def getId(self) -> str: return self._id def setId(self, id: str): self._id = id def getLabel(self) -> str: return self._label def setLabel(self, label: str): self._label = label def getWidth(self) -> int: return self._width def setWidth(self, width: int): self._width = width def getHeight(self) -> int: return self._height def setHeight(self, height: int): self._height = height def toString(self) -> str: return f"MediaSize(id={self._id}, {self._width}x{self._height})" class Resolution(Stub): """Resolution — 打印分辨率""" def __init__(self, id: str = "", label: str = "", horizontal_dpi: int = 300, vertical_dpi: int = 300): super().__init__() self._id = id self._label = label self._horizontal_dpi = horizontal_dpi self._vertical_dpi = vertical_dpi def getId(self) -> str: return self._id def getLabel(self) -> str: return self._label def getHorizontalDpi(self) -> int: return self._horizontal_dpi def getVerticalDpi(self) -> int: return self._vertical_dpi def toString(self) -> str: return f"Resolution(id={self._id}, dpi={self._horizontal_dpi}x{self._vertical_dpi})" class Margins(Stub): """Margins — 打印边距""" def __init__(self, left: int = 0, top: int = 0, right: int = 0, bottom: int = 0): super().__init__() self._left = left self._top = top self._right = right self._bottom = bottom def getLeft(self) -> int: return self._left def getTop(self) -> int: return self._top def getRight(self) -> int: return self._right def getBottom(self) -> int: return self._bottom def toString(self) -> str: return f"Margins(l={self._left}, t={self._top}, r={self._right}, b={self._bottom})" class PaperSize(Stub): """PaperSize — 纸张尺寸""" A0 = 1 A1 = 2 A2 = 3 A3 = 4 A4 = 5 A5 = 6 LETTER = 7 LEGAL = 8 TABLOID = 9 ENVELOPE = 10 PHOTO = 11 def __init__(self, id: int = A4, label: str = "A4", width: int = 210000, height: int = 297000): super().__init__() self._id = id self._label = label self._width = width self._height = height def getId(self) -> int: return self._id def getLabel(self) -> str: return self._label def getWidth(self) -> int: return self._width def getHeight(self) -> int: return self._height def toString(self) -> str: return f"PaperSize(id={self._id}, {self._label})" # ═══════════════════════════════════════════════════════════════ # PrintJobInfo # ═══════════════════════════════════════════════════════════════ class PrintJobInfo(Stub): """PrintJobInfo — 打印作业信息""" STATE_CREATED = 1 STATE_QUEUED = 2 STATE_STARTED = 3 STATE_BLOCKED = 4 STATE_COMPLETED = 5 STATE_FAILED = 6 STATE_CANCELED = 7 def __init__(self, job_id: int = 0, label: str = ""): super().__init__() self._job_id = job_id self._label = label self._state: int = PrintJobInfo.STATE_CREATED self._printer: Optional[PrinterId] = None self._creation_time: int = int(time.time() * 1000) self._copies: int = 1 self._page_count: int = 0 self._media_size: Optional[MediaSize] = None self._resolution: Optional[Resolution] = None self._is_color: bool = True self._is_duplex: bool = False self._progress: float = 0.0 self._status: int = 0 self._status_string: str = "" def getJobId(self) -> int: return self._job_id def setJobId(self, job_id: int): self._job_id = job_id def getLabel(self) -> str: return self._label def setLabel(self, label: str): self._label = label def getState(self) -> int: return self._state def setState(self, state: int): self._state = state def getPrinterId(self) -> Optional[PrinterId]: return self._printer def setPrinterId(self, printer_id: PrinterId): self._printer = printer_id def getCreationTime(self) -> int: return self._creation_time def setCreationTime(self, time_ms: int): self._creation_time = time_ms def getCopies(self) -> int: return self._copies def setCopies(self, copies: int): self._copies = copies def getPageCount(self) -> int: return self._page_count def setPageCount(self, count: int): self._page_count = count def getMediaSize(self) -> Optional[MediaSize]: return self._media_size def setMediaSize(self, size: MediaSize): self._media_size = size def getResolution(self) -> Optional[Resolution]: return self._resolution def setResolution(self, resolution: Resolution): self._resolution = resolution def isColor(self) -> bool: return self._is_color def setColor(self, color: bool): self._is_color = color def isDuplex(self) -> bool: return self._is_duplex def setDuplex(self, duplex: bool): self._is_duplex = duplex def getProgress(self) -> float: return self._progress def setProgress(self, progress: float): self._progress = progress def getStatus(self) -> int: return self._status def setStatus(self, status: int): self._status = status def getStatusString(self) -> str: return self._status_string def setStatusString(self, status_str: str): self._status_string = status_str def toString(self) -> str: return f"PrintJobInfo(id={self._job_id}, label={self._label}, state={self._state})" # ═══════════════════════════════════════════════════════════════ # PrintJob # ═══════════════════════════════════════════════════════════════ class PrintJob(Stub): """PrintJob — 打印作业""" def __init__(self, job_id: int = 0, label: str = ""): super().__init__() self._info = PrintJobInfo(job_id, label) self._document: Optional[PrintDocument] = None self._is_cancelled: bool = False self._is_queued: bool = False self._advanced_options: Dict[str, Any] = {} def getId(self) -> int: return self._info.getJobId() def getInfo(self) -> PrintJobInfo: return self._info def setInfo(self, info: PrintJobInfo): self._info = info def getDocument(self) -> Optional[PrintDocument]: return self._document def setDocument(self, document: PrintDocument): self._document = document def isCancelled(self) -> bool: return self._is_cancelled def isQueued(self) -> bool: return self._is_queued def start(self): """开始打印作业""" self._info.setState(PrintJobInfo.STATE_STARTED) def block(self, reason: str): """阻塞打印作业""" self._info.setState(PrintJobInfo.STATE_BLOCKED) self._info.setStatusString(reason) def complete(self): """完成打印作业""" self._info.setState(PrintJobInfo.STATE_COMPLETED) self._info.setProgress(1.0) def fail(self, error: str): """打印作业失败""" self._info.setState(PrintJobInfo.STATE_FAILED) self._info.setStatusString(error) def cancel(self): """取消打印作业""" self._info.setState(PrintJobInfo.STATE_CANCELED) self._is_cancelled = True def setProgress(self, progress: float): """设置进度""" self._info.setProgress(max(0.0, min(1.0, progress))) def getAdvancedOptions(self) -> Dict[str, Any]: return dict(self._advanced_options) def setAdvancedOption(self, key: str, value: Any): self._advanced_options[key] = value def toString(self) -> str: return f"PrintJob(id={self.getId()}, state={self._info.getState()})" # ═══════════════════════════════════════════════════════════════ # PrintDocument # ═══════════════════════════════════════════════════════════════ class PrintDocument(Stub): """PrintDocument — 打印文档""" def __init__(self, data: bytes = b"", mime_type: str = "application/pdf"): super().__init__() self._data = data self._mime_type = mime_type self._page_count: int = 0 self._title: str = "" self._content_uri: str = "" self._file_path: str = "" def getData(self) -> bytes: return self._data def setData(self, data: bytes): self._data = data def getMimeType(self) -> str: return self._mime_type def setMimeType(self, mime_type: str): self._mime_type = mime_type def getPageCount(self) -> int: return self._page_count def setPageCount(self, count: int): self._page_count = count def getTitle(self) -> str: return self._title def setTitle(self, title: str): self._title = title def getContentUri(self) -> str: return self._content_uri def setContentUri(self, uri: str): self._content_uri = uri def getFilePath(self) -> str: return self._file_path def setFilePath(self, file_path: str): self._file_path = file_path def toString(self) -> str: return f"PrintDocument(title={self._title}, pages={self._page_count})" # ═══════════════════════════════════════════════════════════════ # PrinterDiscoverySession # ═══════════════════════════════════════════════════════════════ class PrinterDiscoverySession(Stub): """PrinterDiscoverySession — 打印机发现会话""" def __init__(self): super().__init__() self._printers: Dict[PrinterId, PrinterInfo] = {} self._tracked_printers: Set[PrinterId] = set() self._is_discovering: bool = False self._is_destroyed: bool = False def onStartPrinterDiscovery(self, priority_list: Optional[List[PrinterId]]): """开始打印机发现""" self._is_discovering = True def onStopPrinterDiscovery(self): """停止打印机发现""" self._is_discovering = False def onValidatePrinters(self, printer_ids: List[PrinterId]): """验证打印机""" pass def onStartPrinterStateTracking(self, printer_id: PrinterId): """开始跟踪打印机状态""" self._tracked_printers.add(printer_id) def onStopPrinterStateTracking(self, printer_id: PrinterId): """停止跟踪打印机状态""" self._tracked_printers.discard(printer_id) def onDestroy(self): """销毁会话""" self._is_destroyed = True self._printers.clear() self._tracked_printers.clear() self._is_discovering = False def addPrinters(self, printers: List[PrinterInfo]): """添加打印机""" for p in printers: self._printers[p.getId()] = p def removePrinters(self, printer_ids: List[PrinterId]): """移除打印机""" for pid in printer_ids: self._printers.pop(pid, None) def getPrinters(self) -> List[PrinterInfo]: """获取打印机列表""" return list(self._printers.values()) def isDestroyed(self) -> bool: return self._is_destroyed def isPrinterDiscoveryStarted(self) -> bool: return self._is_discovering def getTrackedPrinters(self) -> Set[PrinterId]: return set(self._tracked_printers) def toString(self) -> str: return f"PrinterDiscoverySession(printers={len(self._printers)})" # ═══════════════════════════════════════════════════════════════ # CustomPrinterIconCallback # ═══════════════════════════════════════════════════════════════ class CustomPrinterIconCallback(Stub): """CustomPrinterIconCallback — 自定义打印机图标回调""" def __init__(self): super().__init__() self._icon_cache: Dict[PrinterId, bytes] = {} self._callbacks: Dict[PrinterId, Callable] = {} def onCustomPrinterIconLoaded(self, printer_id: PrinterId, icon: bytes): """自定义打印机图标加载回调""" self._icon_cache[printer_id] = icon def requestCustomPrinterIcon(self, printer_id: PrinterId, callback: Callable[[bytes], None]): """请求自定义打印机图标""" if printer_id in self._icon_cache: callback(self._icon_cache[printer_id]) else: self._callbacks[printer_id] = callback def getCachedIcon(self, printer_id: PrinterId) -> Optional[bytes]: return self._icon_cache.get(printer_id) def clearCache(self): self._icon_cache.clear() self._callbacks.clear() def toString(self) -> str: return f"CustomPrinterIconCallback(cached={len(self._icon_cache)})" # ═══════════════════════════════════════════════════════════════ # PrintService # ═══════════════════════════════════════════════════════════════ class PrintService(Stub): """PrintService — 打印服务""" SERVICE_INTERFACE = "android.printservice.PrintService" SERVICE_META_DATA = "android.printservice" EXTRA_PRINT_JOB_INFO = "android.printservice.extra.PRINT_JOB_INFO" EXTRA_SELECT_PRINTER = "android.printservice.extra.SELECT_PRINTER" EXTRA_PRINTER_ID = "android.printservice.extra.PRINTER_ID" EXTRA_PRINTER_INFO = "android.printservice.extra.PRINTER_INFO" EXTRA_PRINT_DOCUMENT_INFO = "android.printservice.extra.PRINT_DOCUMENT_INFO" def __init__(self): super().__init__() self._print_jobs: Dict[int, PrintJob] = {} self._discovery_session: Optional[PrinterDiscoverySession] = None self._icon_callbacks: List[CustomPrinterIconCallback] = [] self._is_connected: bool = False self._printer_infos: Dict[PrinterId, PrinterInfo] = {} def onCreatePrinterDiscoverySession(self) -> PrinterDiscoverySession: """创建打印机发现会话""" self._discovery_session = PrinterDiscoverySession() return self._discovery_session def onPrintJobQueued(self, print_job: PrintJob): """打印作业入队回调""" self._print_jobs[print_job.getId()] = print_job def onCancelPrintJob(self, print_job: PrintJob): """取消打印作业回调""" print_job.cancel() self._print_jobs.pop(print_job.getId(), None) def onConnected(self): """连接回调""" self._is_connected = True def onDisconnected(self): """断开连接回调""" self._is_connected = False def onRequestCancelPrintJob(self, print_job: PrintJob): """请求取消打印作业""" print_job.cancel() def getActivePrintJobs(self) -> List[PrintJob]: """获取活动的打印作业""" return [j for j in self._print_jobs.values() if j.getInfo().getState() in (PrintJobInfo.STATE_QUEUED, PrintJobInfo.STATE_STARTED, PrintJobInfo.STATE_BLOCKED)] def getPrintJobs(self) -> List[PrintJob]: return list(self._print_jobs.values()) def addPrintJob(self, print_job: PrintJob): self._print_jobs[print_job.getId()] = print_job def removePrintJob(self, job_id: int): self._print_jobs.pop(job_id, None) def getDiscoverySession(self) -> Optional[PrinterDiscoverySession]: return self._discovery_session def addPrinterInfo(self, info: PrinterInfo): self._printer_infos[info.getId()] = info def removePrinterInfo(self, printer_id: PrinterId): self._printer_infos.pop(printer_id, None) def getPrinterInfos(self) -> List[PrinterInfo]: return list(self._printer_infos.values()) def isConnected(self) -> bool: return self._is_connected def generatePrintJobId(self) -> int: return int(time.time() * 1000) % 100000 def toString(self) -> str: return f"PrintService(jobs={len(self._print_jobs)})" # ═══════════════════════════════════════════════════════════════ # PrintServiceInfo # ═══════════════════════════════════════════════════════════════ class PrintServiceInfo(Stub): """PrintServiceInfo — 打印服务信息""" def __init__(self, package_name: str = "", service_name: str = ""): super().__init__() self._package_name = package_name self._service_name = service_name self._description: str = "" self._is_enabled: bool = False self._settings_activity: str = "" self._add_printers_activity: str = "" self._advanced_options_activity: str = "" def getPackageName(self) -> str: return self._package_name def setPackageName(self, pkg: str): self._package_name = pkg def getServiceName(self) -> str: return self._service_name def setServiceName(self, name: str): self._service_name = name def getDescription(self) -> str: return self._description def setDescription(self, desc: str): self._description = desc def isEnabled(self) -> bool: return self._is_enabled def setEnabled(self, enabled: bool): self._is_enabled = enabled def getSettingsActivity(self) -> str: return self._settings_activity def setSettingsActivity(self, activity: str): self._settings_activity = activity def toString(self) -> str: return f"PrintServiceInfo(package={self._package_name})" # ═══════════════════════════════════════════════════════════════ # PrintServicePrinter # ═══════════════════════════════════════════════════════════════ class PrintServicePrinter(Stub): """PrintServicePrinter — 打印机辅助类""" TYPE_LOCAL = 0 TYPE_NETWORK = 1 TYPE_BLUETOOTH = 2 TYPE_USB = 3 TYPE_CLOUD = 4 TYPE_VIRTUAL = 5 def __init__(self, printer_id: PrinterId = None, name: str = "", printer_type: int = TYPE_LOCAL): super().__init__() self._id = printer_id or PrinterId() self._name = name self._printer_type = printer_type self._uri: str = "" self._ip_address: str = "" self._port: int = 0 self._location: str = "" def getId(self) -> PrinterId: return self._id def setId(self, printer_id: PrinterId): self._id = printer_id def getName(self) -> str: return self._name def setName(self, name: str): self._name = name def getPrinterType(self) -> int: return self._printer_type def setPrinterType(self, type_: int): self._printer_type = type_ def getUri(self) -> str: return self._uri def setUri(self, uri: str): self._uri = uri def getIpAddress(self) -> str: return self._ip_address def setIpAddress(self, ip: str): self._ip_address = ip def getPort(self) -> int: return self._port def setPort(self, port: int): self._port = port def getLocation(self) -> str: return self._location def setLocation(self, location: str): self._location = location def toString(self) -> str: return f"PrintServicePrinter(name={self._name}, type={self._printer_type})" # ═══════════════════════════════════════════════════════════════ # PrintServiceUtils # ═══════════════════════════════════════════════════════════════ class PrintServiceUtils(Stub): """PrintServiceUtils — 打印服务工具类""" @staticmethod def createPrinterId(package_name: str, local_id: str) -> PrinterId: return PrinterId(package_name, local_id) @staticmethod def createPrinterInfo(printer_id: PrinterId, name: str, status: int) -> PrinterInfo: return PrinterInfo(printer_id, name, status) @staticmethod def createPrintJobInfo(job_id: int, label: str) -> PrintJobInfo: return PrintJobInfo(job_id, label) @staticmethod def createPrintJob(job_id: int, label: str) -> PrintJob: return PrintJob(job_id, label) @staticmethod def createPrintDocument(data: bytes, mime_type: str) -> PrintDocument: return PrintDocument(data, mime_type) @staticmethod def isServiceEnabled(package_name: str) -> bool: return True @staticmethod def getServiceList() -> List[PrintServiceInfo]: return [] @staticmethod def enableService(package_name: str, enabled: bool): pass