新增模块覆盖: 系统层 (14): system, security, print, opengl, renderscript, transition, test, service_notification, service_controls, service_quicksettings, service_wallpaper, service_autofill, service_dreams, service_vr 应用层 (13): app_admin, app_backup, app_job, app_usage, app_role, app_people, appwidget, companion, credentials, mtp, sip, telecom, speech 硬件媒体层 (15): hardware_display, hardware_fingerprint, hardware_usb, hardware_input, media_audiofx, media_browse, media_session, media_tv, media_midi, media_projection, uwb, se, view_autofill, view_textclassifier, view_translation 总计 94 个模块,75,342 行代码
581 lines
19 KiB
Python
581 lines
19 KiB
Python
"""
|
|
Android Print 组件 — PrintManager, PrintJob, PrintDocumentAdapter, PdfPrint 等
|
|
方法签名完全对齐 Android SDK (android.print 包)
|
|
"""
|
|
|
|
import os
|
|
import time
|
|
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
|
|
from .._core import Stub, AndroidOnlyError
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# PrintAttributes
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class PrintAttributes(Stub):
|
|
"""PrintAttributes — 打印属性"""
|
|
|
|
# 颜色模式
|
|
COLOR_MODE_MONOCHROME = 1
|
|
COLOR_MODE_COLOR = 2
|
|
|
|
# 方向
|
|
ORIENTATION_PORTRAIT = 1
|
|
ORIENTATION_LANDSCAPE = 2
|
|
|
|
# 媒质尺寸 ID
|
|
MEDIA_SIZE_UNKNOWN = "unknown"
|
|
MEDIA_SIZE_A4 = "A4"
|
|
MEDIA_SIZE_A3 = "A3"
|
|
MEDIA_SIZE_LETTER = "letter"
|
|
MEDIA_SIZE_LEGAL = "legal"
|
|
MEDIA_SIZE_ISO_A4 = "iso_a4"
|
|
MEDIA_SIZE_ISO_A3 = "iso_a3"
|
|
MEDIA_SIZE_NA_LETTER = "na_letter"
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._media_size: Optional["PrintAttributes.MediaSize"] = None
|
|
self._resolution: Optional["PrintAttributes.Resolution"] = None
|
|
self._min_margins: Optional["PrintAttributes.Margins"] = None
|
|
self._color_mode: int = PrintAttributes.COLOR_MODE_COLOR
|
|
self._orientation: int = PrintAttributes.ORIENTATION_PORTRAIT
|
|
|
|
def getMediaSize(self) -> Optional["MediaSize"]:
|
|
return self._media_size
|
|
|
|
def getResolution(self) -> Optional["Resolution"]:
|
|
return self._resolution
|
|
|
|
def getMinMargins(self) -> Optional["Margins"]:
|
|
return self._min_margins
|
|
|
|
def getColorMode(self) -> int:
|
|
return self._color_mode
|
|
|
|
def getOrientation(self) -> int:
|
|
return self._orientation
|
|
|
|
def isPortrait(self) -> bool:
|
|
return self._orientation == PrintAttributes.ORIENTATION_PORTRAIT
|
|
|
|
def toString(self) -> str:
|
|
return (f"PrintAttributes(colorMode={self._color_mode}, "
|
|
f"orientation={self._orientation})")
|
|
|
|
def hashCode(self) -> int:
|
|
return hash((self._color_mode, self._orientation)) & 0x7FFFFFFF
|
|
|
|
def equals(self, other) -> bool:
|
|
if not isinstance(other, PrintAttributes):
|
|
return False
|
|
return (self._color_mode == other._color_mode
|
|
and self._orientation == other._orientation)
|
|
|
|
# ── 内部嵌套类 ──
|
|
|
|
class MediaSize(Stub):
|
|
"""MediaSize — 媒质尺寸"""
|
|
|
|
def __init__(self, id: str, label: str, width_mils: int, height_mils: int):
|
|
super().__init__()
|
|
self._id = id
|
|
self._label = label
|
|
self._width_mils = width_mils
|
|
self._height_mils = height_mils
|
|
|
|
def getId(self) -> str:
|
|
return self._id
|
|
|
|
def getLabel(self, package_manager: Any = None) -> str:
|
|
return self._label
|
|
|
|
def getWidthMils(self) -> int:
|
|
return self._width_mils
|
|
|
|
def getHeightMils(self) -> int:
|
|
return self._height_mils
|
|
|
|
def asPortrait(self) -> "PrintAttributes.MediaSize":
|
|
if self._width_mils > self._height_mils:
|
|
return PrintAttributes.MediaSize(
|
|
self._id, self._label, self._height_mils, self._width_mils)
|
|
return self
|
|
|
|
def asLandscape(self) -> "PrintAttributes.MediaSize":
|
|
if self._width_mils < self._height_mils:
|
|
return PrintAttributes.MediaSize(
|
|
self._id, self._label, self._height_mils, self._width_mils)
|
|
return self
|
|
|
|
def toString(self) -> str:
|
|
return f"MediaSize({self._id}: {self._width_mils}x{self._height_mils}mils)"
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._id) & 0x7FFFFFFF
|
|
|
|
def equals(self, other) -> bool:
|
|
if not isinstance(other, PrintAttributes.MediaSize):
|
|
return False
|
|
return self._id == other._id
|
|
|
|
class Resolution(Stub):
|
|
"""Resolution — 打印分辨率"""
|
|
|
|
def __init__(self, id: str, label: str, horizontal_dpi: int, vertical_dpi: int):
|
|
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({self._id}: {self._horizontal_dpi}x{self._vertical_dpi}dpi)"
|
|
|
|
class Margins(Stub):
|
|
"""Margins — 页边距"""
|
|
|
|
NO_MARGINS: "PrintAttributes.Margins" = None
|
|
|
|
def __init__(self, left_mils: int, top_mils: int,
|
|
right_mils: int, bottom_mils: int):
|
|
super().__init__()
|
|
self._left_mils = left_mils
|
|
self._top_mils = top_mils
|
|
self._right_mils = right_mils
|
|
self._bottom_mils = bottom_mils
|
|
|
|
def getLeftMils(self) -> int:
|
|
return self._left_mils
|
|
|
|
def getTopMils(self) -> int:
|
|
return self._top_mils
|
|
|
|
def getRightMils(self) -> int:
|
|
return self._right_mils
|
|
|
|
def getBottomMils(self) -> int:
|
|
return self._bottom_mils
|
|
|
|
def toString(self) -> str:
|
|
return f"Margins({self._left_mils},{self._top_mils},{self._right_mils},{self._bottom_mils})"
|
|
|
|
class Builder(Stub):
|
|
"""Builder — PrintAttributes 构建器"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._attrs = PrintAttributes()
|
|
|
|
def setMediaSize(self, media_size: "PrintAttributes.MediaSize") -> "PrintAttributes.Builder":
|
|
self._attrs._media_size = media_size
|
|
return self
|
|
|
|
def setResolution(self, resolution: "PrintAttributes.Resolution") -> "PrintAttributes.Builder":
|
|
self._attrs._resolution = resolution
|
|
return self
|
|
|
|
def setMinMargins(self, margins: "PrintAttributes.Margins") -> "PrintAttributes.Builder":
|
|
self._attrs._min_margins = margins
|
|
return self
|
|
|
|
def setColorMode(self, color_mode: int) -> "PrintAttributes.Builder":
|
|
self._attrs._color_mode = color_mode
|
|
return self
|
|
|
|
def setOrientation(self, orientation: int) -> "PrintAttributes.Builder":
|
|
self._attrs._orientation = orientation
|
|
return self
|
|
|
|
def build(self) -> "PrintAttributes":
|
|
return self._attrs
|
|
|
|
|
|
PrintAttributes.Margins.NO_MARGINS = PrintAttributes.Margins(0, 0, 0, 0)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# 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):
|
|
super().__init__()
|
|
self._label: str = ""
|
|
self._id: Optional[Any] = None
|
|
self._state: int = PrintJobInfo.STATE_CREATED
|
|
self._attributes: Optional[PrintAttributes] = None
|
|
self._copies: int = 1
|
|
self._creation_time: int = int(time.time() * 1000)
|
|
self._canceling: bool = False
|
|
self._pages: Optional[List[int]] = None
|
|
self._has_advanced_options: bool = False
|
|
|
|
def getLabel(self) -> str:
|
|
return self._label
|
|
|
|
def setLabel(self, label: str):
|
|
self._label = label
|
|
|
|
def getId(self) -> Optional[Any]:
|
|
return self._id
|
|
|
|
def getState(self) -> int:
|
|
return self._state
|
|
|
|
def setState(self, state: int):
|
|
self._state = state
|
|
|
|
def getAttributes(self) -> Optional[PrintAttributes]:
|
|
return self._attributes
|
|
|
|
def setAttributes(self, attributes: PrintAttributes):
|
|
self._attributes = attributes
|
|
|
|
def getCopies(self) -> int:
|
|
return self._copies
|
|
|
|
def setCopies(self, copies: int):
|
|
self._copies = copies
|
|
|
|
def getCreationTime(self) -> int:
|
|
return self._creation_time
|
|
|
|
def isCanceling(self) -> bool:
|
|
return self._canceling
|
|
|
|
def getPages(self) -> Optional[List[int]]:
|
|
return self._pages
|
|
|
|
def setPages(self, pages: Optional[List[int]]):
|
|
self._pages = pages
|
|
|
|
def hasAdvancedOptions(self) -> bool:
|
|
return self._has_advanced_options
|
|
|
|
def toString(self) -> str:
|
|
return f"PrintJobInfo(label={self._label}, state={self._state}, copies={self._copies})"
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._id) & 0x7FFFFFFF
|
|
|
|
def equals(self, other) -> bool:
|
|
if not isinstance(other, PrintJobInfo):
|
|
return False
|
|
return self._id == other._id
|
|
|
|
# ── Builder ──
|
|
|
|
class Builder(Stub):
|
|
"""Builder — PrintJobInfo 构建器"""
|
|
|
|
def __init__(self, info: Optional["PrintJobInfo"] = None):
|
|
super().__init__()
|
|
self._info = PrintJobInfo()
|
|
if info:
|
|
self._info._label = info._label
|
|
self._info._id = info._id
|
|
self._info._attributes = info._attributes
|
|
self._info._copies = info._copies
|
|
|
|
def setCopies(self, copies: int) -> "PrintJobInfo.Builder":
|
|
self._info._copies = copies
|
|
return self
|
|
|
|
def setAttributes(self, attributes: PrintAttributes) -> "PrintJobInfo.Builder":
|
|
self._info._attributes = attributes
|
|
return self
|
|
|
|
def setLabel(self, label: str) -> "PrintJobInfo.Builder":
|
|
self._info._label = label
|
|
return self
|
|
|
|
def setPages(self, pages: Optional[List[int]]) -> "PrintJobInfo.Builder":
|
|
self._info._pages = pages
|
|
return self
|
|
|
|
def build(self) -> "PrintJobInfo":
|
|
return self._info
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# PrintJob
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class PrintJob(Stub):
|
|
"""PrintJob — 打印任务"""
|
|
|
|
def __init__(self, info: PrintJobInfo):
|
|
super().__init__()
|
|
self._info = info
|
|
self._cancelled = False
|
|
self._result: Optional[Any] = None
|
|
|
|
def getId(self) -> Optional[Any]:
|
|
return self._info.getId()
|
|
|
|
def getInfo(self) -> PrintJobInfo:
|
|
return self._info
|
|
|
|
def isStarted(self) -> bool:
|
|
return self._info.getState() >= PrintJobInfo.STATE_STARTED
|
|
|
|
def isBlocked(self) -> bool:
|
|
return self._info.getState() == PrintJobInfo.STATE_BLOCKED
|
|
|
|
def isCompleted(self) -> bool:
|
|
return self._info.getState() == PrintJobInfo.STATE_COMPLETED
|
|
|
|
def isFailed(self) -> bool:
|
|
return self._info.getState() == PrintJobInfo.STATE_FAILED
|
|
|
|
def isCancelled(self) -> bool:
|
|
return self._cancelled
|
|
|
|
def cancel(self):
|
|
"""取消打印任务"""
|
|
self._cancelled = True
|
|
self._info.setState(PrintJobInfo.STATE_CANCELED)
|
|
|
|
def restart(self):
|
|
"""重新启动打印任务"""
|
|
self._cancelled = False
|
|
self._info.setState(PrintJobInfo.STATE_QUEUED)
|
|
|
|
def setResult(self, result: Any):
|
|
self._result = result
|
|
|
|
def getResult(self) -> Optional[Any]:
|
|
return self._result
|
|
|
|
def toString(self) -> str:
|
|
return f"PrintJob(id={self.getId()}, state={self._info.getState()})"
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self.getId()) & 0x7FFFFFFF
|
|
|
|
def equals(self, other) -> bool:
|
|
if not isinstance(other, PrintJob):
|
|
return False
|
|
return self.getId() == other.getId()
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# PrintDocumentAdapter
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class PrintDocumentAdapter(Stub):
|
|
"""PrintDocumentAdapter — 打印文档适配器"""
|
|
|
|
EXTRA_PRINT_PREVIEW = "EXTRA_PRINT_PREVIEW"
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
def onStart(self):
|
|
"""打印开始回调"""
|
|
pass
|
|
|
|
def onFinish(self):
|
|
"""打印结束回调"""
|
|
pass
|
|
|
|
def onLayout(self, old_attributes: PrintAttributes, new_attributes: PrintAttributes,
|
|
cancellation_signal: Any, callback: "PrintDocumentAdapterLayoutCallback",
|
|
metadata: Any):
|
|
"""布局回调 - 桌面实现直接完成"""
|
|
if callback:
|
|
callback.onLayoutFinished(self, new_attributes)
|
|
|
|
def onWrite(self, pages: List[int], destination: Any,
|
|
cancellation_signal: Any,
|
|
callback: "PrintDocumentAdapterWriteCallback"):
|
|
"""写入回调 - 桌面实现直接完成"""
|
|
if callback:
|
|
callback.onWriteFinished(pages)
|
|
|
|
|
|
class PrintDocumentAdapterLayoutCallback(Stub):
|
|
"""PrintDocumentAdapterLayoutCallback — 布局回调"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
def onLayoutFinished(self, adapter: PrintDocumentAdapter, attributes: PrintAttributes):
|
|
"""布局完成"""
|
|
pass
|
|
|
|
def onLayoutFailed(self, error: Optional[str] = None):
|
|
"""布局失败"""
|
|
pass
|
|
|
|
def onLayoutCancelled(self):
|
|
"""布局取消"""
|
|
pass
|
|
|
|
|
|
class PrintDocumentAdapterWriteCallback(Stub):
|
|
"""PrintDocumentAdapterWriteCallback — 写入回调"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
def onWriteFinished(self, pages: List[int]):
|
|
"""写入完成"""
|
|
pass
|
|
|
|
def onWriteFailed(self, error: Optional[str] = None):
|
|
"""写入失败"""
|
|
pass
|
|
|
|
def onWriteCancelled(self):
|
|
"""写入取消"""
|
|
pass
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# PdfPrint
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class PdfPrint(Stub):
|
|
"""PdfPrint — PDF 打印辅助类"""
|
|
|
|
def __init__(self, attributes: Optional[PrintAttributes] = None):
|
|
super().__init__()
|
|
self._attributes = attributes or PrintAttributes()
|
|
|
|
def getAttributes(self) -> PrintAttributes:
|
|
return self._attributes
|
|
|
|
def setAttributes(self, attributes: PrintAttributes):
|
|
self._attributes = attributes
|
|
|
|
def print(self, print_adapter: PrintDocumentAdapter, output_path: str) -> bool:
|
|
"""执行 PDF 打印 (桌面实现: 写入文件)"""
|
|
try:
|
|
os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
|
|
# 在桌面环境, PDF 打印由适配器的 onWrite 实现处理
|
|
callback = PrintDocumentAdapterWriteCallback()
|
|
print_adapter.onWrite([1], output_path, None, callback)
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
def toString(self) -> str:
|
|
return f"PdfPrint(attributes={self._attributes})"
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(str(self._attributes)) & 0x7FFFFFFF
|
|
|
|
def equals(self, other) -> bool:
|
|
if not isinstance(other, PdfPrint):
|
|
return False
|
|
return str(self._attributes) == str(other._attributes)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# PrintManager
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class PrintManager(Stub):
|
|
"""PrintManager — 打印管理器"""
|
|
|
|
# 打印任务 ID 生成
|
|
_next_job_id = 1
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._context = context
|
|
self._print_jobs: Dict[str, PrintJob] = {}
|
|
|
|
def getPrintJobs(self) -> List[PrintJob]:
|
|
"""获取打印任务列表"""
|
|
return list(self._print_jobs.values())
|
|
|
|
def print(self, job_name: str, document_adapter: PrintDocumentAdapter,
|
|
attributes: Optional[PrintAttributes] = None) -> PrintJob:
|
|
"""提交打印任务"""
|
|
info = PrintJobInfo()
|
|
info.setLabel(job_name)
|
|
info.setAttributes(attributes or PrintAttributes())
|
|
info.setState(PrintJobInfo.STATE_QUEUED)
|
|
|
|
job_id = f"print_job_{PrintManager._next_job_id}"
|
|
PrintManager._next_job_id += 1
|
|
|
|
job = PrintJob(info)
|
|
self._print_jobs[job_id] = job
|
|
|
|
# 自动触发布局和写入
|
|
cancellation_signal = type("CancellationSignal", (), {"isCanceled": lambda: False})()
|
|
document_adapter.onStart()
|
|
layout_cb = PrintDocumentAdapterLayoutCallback()
|
|
document_adapter.onLayout(None, info.getAttributes(),
|
|
cancellation_signal, layout_cb, None)
|
|
|
|
info.setState(PrintJobInfo.STATE_COMPLETED)
|
|
document_adapter.onFinish()
|
|
|
|
return job
|
|
|
|
def getPrintJobById(self, job_id: str) -> Optional[PrintJob]:
|
|
"""根据 ID 获取打印任务"""
|
|
return self._print_jobs.get(job_id)
|
|
|
|
def cancelPrintJob(self, job_id: str) -> bool:
|
|
"""取消打印任务"""
|
|
job = self._print_jobs.get(job_id)
|
|
if job:
|
|
job.cancel()
|
|
return True
|
|
return False
|
|
|
|
def toString(self) -> str:
|
|
return f"PrintManager(jobs={len(self._print_jobs)})"
|
|
|
|
def hashCode(self) -> int:
|
|
return id(self) & 0x7FFFFFFF
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# __all__
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
__all__ = [
|
|
"PrintManager",
|
|
"PrintJob",
|
|
"PrintJobInfo",
|
|
"PrintDocumentAdapter",
|
|
"PrintAttributes",
|
|
"PrintDocumentAdapterLayoutCallback",
|
|
"PrintDocumentAdapterWriteCallback",
|
|
"PdfPrint",
|
|
] |