""" 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