- android/ 167个模块 (app/content/os/view/widget/graphics/media/hardware/net/telephony/provider等) - java/ 35个模块 (lang/io/util/net/nio/security/crypto/sql/beans/rmi/awt等) - javax/ 4个模块 (net/crypto/security/sql) - org/ 5个模块 (json/w3c_dom/xml_sax/xml_pull/apache_http) - dalvik/ 1个模块 (system) - ~3053个类/接口,方法签名完全对齐Android SDK - 桌面跨平台运行 (Windows/macOS/Linux) - 修复所有导入错误和Python关键字冲突
633 lines
22 KiB
Python
633 lines
22 KiB
Python
"""
|
|
Android media.remoteblob 包 — RemoteBlobManager, RemoteBlob, RemoteBlobStore,
|
|
RemoteBlobReader, RemoteBlobQuery, RemoteBlobResult
|
|
方法签名完全对齐 Android SDK
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Dict, List, Optional, Tuple, Callable
|
|
|
|
from .._core import Stub, AndroidOnlyError
|
|
|
|
__all__ = [
|
|
"RemoteBlobManager",
|
|
"RemoteBlob",
|
|
"RemoteBlobStore",
|
|
"RemoteBlobReader",
|
|
"RemoteBlobQuery",
|
|
"RemoteBlobResult",
|
|
]
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# 常量
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
QUERY_TYPE_ALL = 0
|
|
QUERY_TYPE_BY_ID = 1
|
|
QUERY_TYPE_BY_OWNER = 2
|
|
QUERY_TYPE_BY_TAG = 3
|
|
QUERY_TYPE_BY_TIMESTAMP = 4
|
|
QUERY_TYPE_BY_SIZE = 5
|
|
QUERY_TYPE_BY_MIME_TYPE = 6
|
|
|
|
STATUS_SUCCESS = 0
|
|
STATUS_PENDING = 1
|
|
STATUS_FAILED = 2
|
|
STATUS_CANCELLED = 3
|
|
STATUS_NOT_FOUND = 4
|
|
STATUS_ACCESS_DENIED = 5
|
|
STATUS_INSUFFICIENT_SPACE = 6
|
|
STATUS_TIMEOUT = 7
|
|
STATUS_INVALID_ARGUMENT = 8
|
|
STATUS_EXPIRED = 9
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# RemoteBlob
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class RemoteBlob(Stub):
|
|
"""RemoteBlob — 远程 Blob 数据块"""
|
|
|
|
def __init__(self, id: str = "", owner: str = "",
|
|
tag: str = "", data: bytes = b"",
|
|
mimeType: str = "application/octet-stream",
|
|
size: int = 0, timestamp: int = 0,
|
|
metadata: Dict[str, str] = None,
|
|
expiryTimestamp: int = 0):
|
|
super().__init__()
|
|
self._id = id
|
|
self._owner = owner
|
|
self._tag = tag
|
|
self._data = data
|
|
self._mime_type = mimeType
|
|
self._size = size if size > 0 else len(data)
|
|
self._timestamp = timestamp
|
|
self._metadata = metadata or {}
|
|
self._expiry_timestamp = expiryTimestamp
|
|
|
|
def getId(self) -> str:
|
|
return self._id
|
|
|
|
def getOwner(self) -> str:
|
|
return self._owner
|
|
|
|
def getTag(self) -> str:
|
|
return self._tag
|
|
|
|
def getData(self) -> bytes:
|
|
return self._data
|
|
|
|
def getMimeType(self) -> str:
|
|
return self._mime_type
|
|
|
|
def getSize(self) -> int:
|
|
return self._size
|
|
|
|
def getTimestamp(self) -> int:
|
|
return self._timestamp
|
|
|
|
def getMetadata(self) -> Dict[str, str]:
|
|
return dict(self._metadata)
|
|
|
|
def getExpiryTimestamp(self) -> int:
|
|
return self._expiry_timestamp
|
|
|
|
def isExpired(self) -> bool:
|
|
import time
|
|
return 0 < self._expiry_timestamp < int(time.time() * 1000)
|
|
|
|
def setMetadata(self, key: str, value: str):
|
|
self._metadata[key] = value
|
|
|
|
def getMetadataValue(self, key: str) -> Optional[str]:
|
|
return self._metadata.get(key)
|
|
|
|
class Builder(Stub):
|
|
"""RemoteBlob.Builder — Blob 构建器"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._id = ""
|
|
self._owner = ""
|
|
self._tag = ""
|
|
self._data = b""
|
|
self._mime_type = "application/octet-stream"
|
|
self._metadata: Dict[str, str] = {}
|
|
self._expiry_timestamp = 0
|
|
|
|
def setId(self, id: str) -> "RemoteBlob.Builder":
|
|
self._id = id
|
|
return self
|
|
|
|
def setOwner(self, owner: str) -> "RemoteBlob.Builder":
|
|
self._owner = owner
|
|
return self
|
|
|
|
def setTag(self, tag: str) -> "RemoteBlob.Builder":
|
|
self._tag = tag
|
|
return self
|
|
|
|
def setData(self, data: bytes) -> "RemoteBlob.Builder":
|
|
self._data = data
|
|
return self
|
|
|
|
def setMimeType(self, mimeType: str) -> "RemoteBlob.Builder":
|
|
self._mime_type = mimeType
|
|
return self
|
|
|
|
def setMetadata(self, metadata: Dict[str, str]) -> "RemoteBlob.Builder":
|
|
self._metadata = metadata
|
|
return self
|
|
|
|
def setExpiryTimestamp(self, expiryTimestamp: int) -> "RemoteBlob.Builder":
|
|
self._expiry_timestamp = expiryTimestamp
|
|
return self
|
|
|
|
def build(self) -> "RemoteBlob":
|
|
return RemoteBlob(
|
|
id=self._id, owner=self._owner, tag=self._tag,
|
|
data=self._data, mimeType=self._mime_type,
|
|
metadata=self._metadata,
|
|
expiryTimestamp=self._expiry_timestamp,
|
|
)
|
|
|
|
def toString(self) -> str:
|
|
return f"RemoteBlob(id={self._id}, size={self._size}, tag={self._tag})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, RemoteBlob):
|
|
return False
|
|
return self._id == other._id
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._id) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# RemoteBlobQuery
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class RemoteBlobQuery(Stub):
|
|
"""RemoteBlobQuery — 远程 Blob 查询"""
|
|
|
|
def __init__(self, queryType: int = QUERY_TYPE_ALL,
|
|
queryValue: str = "",
|
|
ownerFilter: str = "",
|
|
tagFilter: str = "",
|
|
mimeTypeFilter: str = "",
|
|
minSize: int = 0, maxSize: int = 0,
|
|
startTimestamp: int = 0, endTimestamp: int = 0,
|
|
offset: int = 0, limit: int = 100,
|
|
includeExpired: bool = False):
|
|
super().__init__()
|
|
self._query_type = queryType
|
|
self._query_value = queryValue
|
|
self._owner_filter = ownerFilter
|
|
self._tag_filter = tagFilter
|
|
self._mime_type_filter = mimeTypeFilter
|
|
self._min_size = minSize
|
|
self._max_size = maxSize
|
|
self._start_timestamp = startTimestamp
|
|
self._end_timestamp = endTimestamp
|
|
self._offset = offset
|
|
self._limit = limit
|
|
self._include_expired = includeExpired
|
|
|
|
def getQueryType(self) -> int:
|
|
return self._query_type
|
|
|
|
def getQueryValue(self) -> str:
|
|
return self._query_value
|
|
|
|
def getOwnerFilter(self) -> str:
|
|
return self._owner_filter
|
|
|
|
def getTagFilter(self) -> str:
|
|
return self._tag_filter
|
|
|
|
def getMimeTypeFilter(self) -> str:
|
|
return self._mime_type_filter
|
|
|
|
def getMinSize(self) -> int:
|
|
return self._min_size
|
|
|
|
def getMaxSize(self) -> int:
|
|
return self._max_size
|
|
|
|
def getStartTimestamp(self) -> int:
|
|
return self._start_timestamp
|
|
|
|
def getEndTimestamp(self) -> int:
|
|
return self._end_timestamp
|
|
|
|
def getOffset(self) -> int:
|
|
return self._offset
|
|
|
|
def getLimit(self) -> int:
|
|
return self._limit
|
|
|
|
def includeExpired(self) -> bool:
|
|
return self._include_expired
|
|
|
|
class Builder(Stub):
|
|
"""RemoteBlobQuery.Builder — 查询构建器"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._query_type = QUERY_TYPE_ALL
|
|
self._query_value = ""
|
|
self._owner_filter = ""
|
|
self._tag_filter = ""
|
|
self._mime_type_filter = ""
|
|
self._min_size = 0
|
|
self._max_size = 0
|
|
self._start_timestamp = 0
|
|
self._end_timestamp = 0
|
|
self._offset = 0
|
|
self._limit = 100
|
|
self._include_expired = False
|
|
|
|
def setQueryType(self, queryType: int) -> "RemoteBlobQuery.Builder":
|
|
self._query_type = queryType
|
|
return self
|
|
|
|
def setQueryValue(self, queryValue: str) -> "RemoteBlobQuery.Builder":
|
|
self._query_value = queryValue
|
|
return self
|
|
|
|
def setOwnerFilter(self, owner: str) -> "RemoteBlobQuery.Builder":
|
|
self._owner_filter = owner
|
|
return self
|
|
|
|
def setTagFilter(self, tag: str) -> "RemoteBlobQuery.Builder":
|
|
self._tag_filter = tag
|
|
return self
|
|
|
|
def setMimeTypeFilter(self, mimeType: str) -> "RemoteBlobQuery.Builder":
|
|
self._mime_type_filter = mimeType
|
|
return self
|
|
|
|
def setSizeRange(self, minSize: int, maxSize: int) -> "RemoteBlobQuery.Builder":
|
|
self._min_size = minSize
|
|
self._max_size = maxSize
|
|
return self
|
|
|
|
def setTimestampRange(self, start: int, end: int) -> "RemoteBlobQuery.Builder":
|
|
self._start_timestamp = start
|
|
self._end_timestamp = end
|
|
return self
|
|
|
|
def setOffset(self, offset: int) -> "RemoteBlobQuery.Builder":
|
|
self._offset = offset
|
|
return self
|
|
|
|
def setLimit(self, limit: int) -> "RemoteBlobQuery.Builder":
|
|
self._limit = limit
|
|
return self
|
|
|
|
def setIncludeExpired(self, include: bool) -> "RemoteBlobQuery.Builder":
|
|
self._include_expired = include
|
|
return self
|
|
|
|
def build(self) -> "RemoteBlobQuery":
|
|
return RemoteBlobQuery(
|
|
queryType=self._query_type, queryValue=self._query_value,
|
|
ownerFilter=self._owner_filter, tagFilter=self._tag_filter,
|
|
mimeTypeFilter=self._mime_type_filter,
|
|
minSize=self._min_size, maxSize=self._max_size,
|
|
startTimestamp=self._start_timestamp,
|
|
endTimestamp=self._end_timestamp,
|
|
offset=self._offset, limit=self._limit,
|
|
includeExpired=self._include_expired,
|
|
)
|
|
|
|
def toString(self) -> str:
|
|
return f"RemoteBlobQuery(type={self._query_type}, limit={self._limit})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, RemoteBlobQuery):
|
|
return False
|
|
return (self._query_type == other._query_type and
|
|
self._query_value == other._query_value)
|
|
|
|
def hashCode(self) -> int:
|
|
return hash((self._query_type, self._query_value)) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# RemoteBlobResult
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class RemoteBlobResult(Stub):
|
|
"""RemoteBlobResult — 远程 Blob 查询结果"""
|
|
|
|
def __init__(self, status: int = STATUS_SUCCESS,
|
|
blobs: List[RemoteBlob] = None,
|
|
totalCount: int = 0, errorMessage: str = ""):
|
|
super().__init__()
|
|
self._status = status
|
|
self._blobs = blobs or []
|
|
self._total_count = totalCount if totalCount > 0 else len(self._blobs)
|
|
self._error_message = errorMessage
|
|
|
|
def getStatus(self) -> int:
|
|
return self._status
|
|
|
|
def getBlobs(self) -> List[RemoteBlob]:
|
|
return list(self._blobs)
|
|
|
|
def getTotalCount(self) -> int:
|
|
return self._total_count
|
|
|
|
def getErrorMessage(self) -> str:
|
|
return self._error_message
|
|
|
|
def isSuccess(self) -> bool:
|
|
return self._status == STATUS_SUCCESS
|
|
|
|
def toString(self) -> str:
|
|
return f"RemoteBlobResult(status={self._status}, count={len(self._blobs)})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, RemoteBlobResult):
|
|
return False
|
|
return self._status == other._status
|
|
|
|
def hashCode(self) -> int:
|
|
return self._status & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# RemoteBlobStore
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class RemoteBlobStore(Stub):
|
|
"""RemoteBlobStore — 远程 Blob 存储"""
|
|
|
|
def __init__(self, name: str = "default",
|
|
basePath: str = "",
|
|
maxCapacity: int = 100 * 1024 * 1024):
|
|
super().__init__()
|
|
self._name = name
|
|
self._base_path = basePath
|
|
self._max_capacity = maxCapacity
|
|
self._blobs: Dict[str, RemoteBlob] = {}
|
|
self._current_size = 0
|
|
|
|
def getName(self) -> str:
|
|
return self._name
|
|
|
|
def getBasePath(self) -> str:
|
|
return self._base_path
|
|
|
|
def getMaxCapacity(self) -> int:
|
|
return self._max_capacity
|
|
|
|
def getCurrentSize(self) -> int:
|
|
return self._current_size
|
|
|
|
def put(self, blob: RemoteBlob) -> int:
|
|
if blob.getId() in self._blobs:
|
|
old = self._blobs[blob.getId()]
|
|
self._current_size -= old.getSize()
|
|
self._blobs[blob.getId()] = blob
|
|
self._current_size += blob.getSize()
|
|
return STATUS_SUCCESS
|
|
|
|
def get(self, id: str) -> Optional[RemoteBlob]:
|
|
return self._blobs.get(id)
|
|
|
|
def remove(self, id: str) -> int:
|
|
blob = self._blobs.pop(id, None)
|
|
if blob:
|
|
self._current_size -= blob.getSize()
|
|
return STATUS_SUCCESS
|
|
return STATUS_NOT_FOUND
|
|
|
|
def query(self, query: RemoteBlobQuery) -> RemoteBlobResult:
|
|
results = list(self._blobs.values())
|
|
if query.getQueryType() == QUERY_TYPE_BY_ID and query.getQueryValue():
|
|
results = [b for b in results if b.getId() == query.getQueryValue()]
|
|
elif query.getQueryType() == QUERY_TYPE_BY_OWNER and query.getOwnerFilter():
|
|
results = [b for b in results if b.getOwner() == query.getOwnerFilter()]
|
|
elif query.getQueryType() == QUERY_TYPE_BY_TAG and query.getTagFilter():
|
|
results = [b for b in results if b.getTag() == query.getTagFilter()]
|
|
elif query.getQueryType() == QUERY_TYPE_BY_MIME_TYPE and query.getMimeTypeFilter():
|
|
results = [b for b in results if b.getMimeType() == query.getMimeTypeFilter()]
|
|
if not query.includeExpired():
|
|
results = [b for b in results if not b.isExpired()]
|
|
offset = query.getOffset()
|
|
limit = query.getLimit()
|
|
total = len(results)
|
|
results = results[offset:offset + limit]
|
|
return RemoteBlobResult(status=STATUS_SUCCESS, blobs=results, totalCount=total)
|
|
|
|
def clear(self):
|
|
self._blobs.clear()
|
|
self._current_size = 0
|
|
|
|
def contains(self, id: str) -> bool:
|
|
return id in self._blobs
|
|
|
|
def getSize(self) -> int:
|
|
return len(self._blobs)
|
|
|
|
def getCapacityRemaining(self) -> int:
|
|
return self._max_capacity - self._current_size
|
|
|
|
def toString(self) -> str:
|
|
return f"RemoteBlobStore(name={self._name}, count={len(self._blobs)})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, RemoteBlobStore):
|
|
return False
|
|
return self._name == other._name
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._name) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# RemoteBlobReader
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class RemoteBlobReader(Stub):
|
|
"""RemoteBlobReader — 远程 Blob 读取器"""
|
|
|
|
def __init__(self, store: Optional[RemoteBlobStore] = None,
|
|
blobId: str = "", bufferSize: int = 8192):
|
|
super().__init__()
|
|
self._store = store
|
|
self._blob_id = blobId
|
|
self._buffer_size = bufferSize
|
|
self._position = 0
|
|
self._open = False
|
|
self._blob: Optional[RemoteBlob] = None
|
|
|
|
def open(self) -> int:
|
|
if not self._store:
|
|
return STATUS_FAILED
|
|
self._blob = self._store.get(self._blob_id)
|
|
if not self._blob:
|
|
return STATUS_NOT_FOUND
|
|
self._position = 0
|
|
self._open = True
|
|
return STATUS_SUCCESS
|
|
|
|
def read(self, size: int = -1) -> Optional[bytes]:
|
|
if not self._open or not self._blob:
|
|
return None
|
|
data = self._blob.getData()
|
|
if self._position >= len(data):
|
|
return b""
|
|
if size < 0:
|
|
result = data[self._position:]
|
|
self._position = len(data)
|
|
else:
|
|
result = data[self._position:self._position + size]
|
|
self._position += len(result)
|
|
return result
|
|
|
|
def readFully(self) -> Optional[bytes]:
|
|
if not self._open or not self._blob:
|
|
return None
|
|
return self._blob.getData()
|
|
|
|
def seek(self, position: int) -> int:
|
|
if not self._blob:
|
|
return STATUS_FAILED
|
|
if position < 0 or position > len(self._blob.getData()):
|
|
return STATUS_INVALID_ARGUMENT
|
|
self._position = position
|
|
return STATUS_SUCCESS
|
|
|
|
def getPosition(self) -> int:
|
|
return self._position
|
|
|
|
def getRemaining(self) -> int:
|
|
if not self._blob:
|
|
return 0
|
|
return max(0, len(self._blob.getData()) - self._position)
|
|
|
|
def close(self):
|
|
self._open = False
|
|
self._blob = None
|
|
self._position = 0
|
|
|
|
def isOpen(self) -> bool:
|
|
return self._open
|
|
|
|
def toString(self) -> str:
|
|
return f"RemoteBlobReader(blob={self._blob_id}, open={self._open})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return id(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# RemoteBlobManager
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class RemoteBlobManager(Stub):
|
|
"""RemoteBlobManager — 远程 Blob 管理器"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._stores: Dict[str, RemoteBlobStore] = {}
|
|
self._default_store = RemoteBlobStore("default")
|
|
self._stores["default"] = self._default_store
|
|
self._listeners: Dict[int, Callable] = {}
|
|
|
|
def createStore(self, name: str, basePath: str = "",
|
|
maxCapacity: int = 100 * 1024 * 1024) -> RemoteBlobStore:
|
|
if name in self._stores:
|
|
raise IllegalArgumentException(f"Store '{name}' already exists")
|
|
store = RemoteBlobStore(name=name, basePath=basePath, maxCapacity=maxCapacity)
|
|
self._stores[name] = store
|
|
return store
|
|
|
|
def getStore(self, name: str) -> Optional[RemoteBlobStore]:
|
|
return self._stores.get(name)
|
|
|
|
def deleteStore(self, name: str) -> int:
|
|
if name not in self._stores:
|
|
return STATUS_NOT_FOUND
|
|
self._stores[name].clear()
|
|
del self._stores[name]
|
|
return STATUS_SUCCESS
|
|
|
|
def getDefaultStore(self) -> RemoteBlobStore:
|
|
return self._default_store
|
|
|
|
def listStores(self) -> List[str]:
|
|
return list(self._stores.keys())
|
|
|
|
def openReader(self, storeName: str, blobId: str) -> Optional[RemoteBlobReader]:
|
|
store = self._stores.get(storeName)
|
|
if not store:
|
|
return None
|
|
reader = RemoteBlobReader(store=store, blobId=blobId)
|
|
status = reader.open()
|
|
return reader if status == STATUS_SUCCESS else None
|
|
|
|
def put(self, blob: RemoteBlob, storeName: str = "default") -> int:
|
|
store = self._stores.get(storeName)
|
|
if not store:
|
|
return STATUS_NOT_FOUND
|
|
return store.put(blob)
|
|
|
|
def get(self, blobId: str, storeName: str = "default") -> Optional[RemoteBlob]:
|
|
store = self._stores.get(storeName)
|
|
if not store:
|
|
return None
|
|
return store.get(blobId)
|
|
|
|
def remove(self, blobId: str, storeName: str = "default") -> int:
|
|
store = self._stores.get(storeName)
|
|
if not store:
|
|
return STATUS_NOT_FOUND
|
|
return store.remove(blobId)
|
|
|
|
def query(self, query: RemoteBlobQuery, storeName: str = "default") -> RemoteBlobResult:
|
|
store = self._stores.get(storeName)
|
|
if not store:
|
|
return RemoteBlobResult(status=STATUS_NOT_FOUND)
|
|
return store.query(query)
|
|
|
|
def registerCallback(self, callback: Callable) -> int:
|
|
lid = id(callback)
|
|
self._listeners[lid] = callback
|
|
return STATUS_SUCCESS
|
|
|
|
def unregisterCallback(self, callback: Callable) -> int:
|
|
lid = id(callback)
|
|
return STATUS_SUCCESS if self._listeners.pop(lid, None) else STATUS_NOT_FOUND
|
|
|
|
def getStoreNames(self) -> List[str]:
|
|
return self.listStores()
|
|
|
|
def getTotalSize(self) -> int:
|
|
return sum(s.getCurrentSize() for s in self._stores.values())
|
|
|
|
def getTotalCount(self) -> int:
|
|
return sum(s.getSize() for s in self._stores.values())
|
|
|
|
def clearAll(self):
|
|
for store in self._stores.values():
|
|
store.clear()
|
|
|
|
def toString(self) -> str:
|
|
return (f"RemoteBlobManager(stores={len(self._stores)}, "
|
|
f"totalBlobs={self.getTotalCount()})")
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return id(self) & 0x7FFFFFFF |