feat: 第三轮深度完善 — SQLite/JSON/网络/媒体/文本 真实实现
SQLite真实数据库: - SQLiteDatabase: execSQL/insert/insertOrThrow/replace/update/delete/query 用sqlite3 - 修复getVersion/setVersion用PRAGMA user_version - 修复getMaximumSize/getPageSize/setMaximumSize/setPageSize用PRAGMA - SQLiteStatement: execute/simpleQueryForLong/simpleQueryForString/execQuery - SQLiteOpenHelper: onCreate/onUpgrade/onDowngrade回调正确触发 - 修复SQLiteCursor.moveToPosition越界处理 - 修复ContentValues值对提取bug JSON真实实现: - JSONObject: isNull/append/putOnce/putOpt/getNames - JSONArray: 完整put/get/opt/length/toString 网络真实实现: - NetworkInterface: getByName/getNetworkInterfaces/getHardwareAddress 用/sys/class/net+fcntl.ioctl - InterfaceAddress: 新增类 - ConnectivityManager: getActiveNetwork/getAllNetworks 用LinuxNetwork - WifiManager: getScanResults用nmcli, setWifiEnabled用nmcli/rfkill - BluetoothAdapter: getAddress/getName 用bluetoothctl 媒体真实实现: - MediaPlayer: prepare检查文件, getCurrentPosition实时计算, getDuration/getVideoWidth用ffprobe - MediaRecorder: setVideoSize/setVideoFrameRate/setVideoEncodingBitRate配置存储 - MediaMetadataRetriever: extractMetadata用ffprobe, getFrameAtTime用ffmpeg, getEmbeddedPicture用ffmpeg 文本真实实现: - TextUtils: split用re.split, concat/indexOf/lastIndexOf/urlEncode/urlDecode - SpannableString: setSpan/getSpans/getSpanFlags/nextSpanTransition 真实实现 - SpannableStringBuilder: 修复append重载bug Provider增强: - Settings: putBoolean/getBoolean - MediaStore: getContentUri/VOLUME常量 - ContactsContract: 补全常量和CommonDataKinds Util增强: - Log: w/e/wtf支持Throwable参数, 修复wtf重复定义bug - TypedValue: 修复complexToDimension/complexToFloat/getFloat实现 修复: - __init__.py: 补全NetworkInterface/InetAddress/MediaMetadataRetriever/SpannableStringBuilder导出 统计: 221模块, 3666类, 31005方法, 183529行
This commit is contained in:
1 parent
4235f8692c
commit
fbb8263b13
13 files changed
+1373
-146
No files matched your search
@@ -181,9 +181,13 @@ class SQLiteProgram(SQLiteClosable):
|
||||
def clearBindings(self):
|
||||
self._bindings.clear()
|
||||
|
||||
def _ordered_bind_args(self) -> list:
|
||||
"""按绑定索引升序返回参数列表(1-based 索引排序)。"""
|
||||
return [self._bindings[k] for k in sorted(self._bindings.keys())]
|
||||
|
||||
def _bind_parameters(self, cursor: _sqlite3.Cursor):
|
||||
for idx, val in sorted(self._bindings.items()):
|
||||
cursor.execute(f"PRAGMA dummy={idx}")
|
||||
for idx in sorted(self._bindings.keys()):
|
||||
cursor.bind(idx, self._bindings[idx])
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
@@ -199,16 +203,18 @@ class SQLiteStatement(SQLiteProgram):
|
||||
def execute(self):
|
||||
try:
|
||||
conn = self._db._acquire_connection()
|
||||
cursor = conn.execute(self._sql, list(self._bindings.values()))
|
||||
conn.commit()
|
||||
conn.execute(self._sql, self._ordered_bind_args())
|
||||
if not self._db._in_transaction:
|
||||
conn.commit()
|
||||
except _sqlite3.DatabaseError as e:
|
||||
raise _map_sqlite_error(str(e), self._sql)
|
||||
|
||||
def executeUpdateDelete(self) -> int:
|
||||
try:
|
||||
conn = self._db._acquire_connection()
|
||||
cursor = conn.execute(self._sql, list(self._bindings.values()))
|
||||
conn.commit()
|
||||
cursor = conn.execute(self._sql, self._ordered_bind_args())
|
||||
if not self._db._in_transaction:
|
||||
conn.commit()
|
||||
return cursor.rowcount
|
||||
except _sqlite3.DatabaseError as e:
|
||||
raise _map_sqlite_error(str(e), self._sql)
|
||||
@@ -216,8 +222,9 @@ class SQLiteStatement(SQLiteProgram):
|
||||
def executeInsert(self) -> int:
|
||||
try:
|
||||
conn = self._db._acquire_connection()
|
||||
cursor = conn.execute(self._sql, list(self._bindings.values()))
|
||||
conn.commit()
|
||||
cursor = conn.execute(self._sql, self._ordered_bind_args())
|
||||
if not self._db._in_transaction:
|
||||
conn.commit()
|
||||
return cursor.lastrowid
|
||||
except _sqlite3.DatabaseError as e:
|
||||
raise _map_sqlite_error(str(e), self._sql)
|
||||
@@ -225,9 +232,10 @@ class SQLiteStatement(SQLiteProgram):
|
||||
def simpleQueryForLong(self) -> int:
|
||||
try:
|
||||
conn = self._db._acquire_connection()
|
||||
cursor = conn.execute(self._sql, list(self._bindings.values()))
|
||||
cursor = conn.execute(self._sql, self._ordered_bind_args())
|
||||
row = cursor.fetchone()
|
||||
conn.commit()
|
||||
if not self._db._in_transaction:
|
||||
conn.commit()
|
||||
if row:
|
||||
return int(row[0]) if row[0] is not None else 0
|
||||
raise SQLiteDoneException("no results")
|
||||
@@ -237,15 +245,43 @@ class SQLiteStatement(SQLiteProgram):
|
||||
def simpleQueryForString(self) -> Optional[str]:
|
||||
try:
|
||||
conn = self._db._acquire_connection()
|
||||
cursor = conn.execute(self._sql, list(self._bindings.values()))
|
||||
cursor = conn.execute(self._sql, self._ordered_bind_args())
|
||||
row = cursor.fetchone()
|
||||
conn.commit()
|
||||
if not self._db._in_transaction:
|
||||
conn.commit()
|
||||
if row:
|
||||
return str(row[0]) if row[0] is not None else None
|
||||
raise SQLiteDoneException("no results")
|
||||
except _sqlite3.DatabaseError as e:
|
||||
raise _map_sqlite_error(str(e), self._sql)
|
||||
|
||||
def simpleQueryForBlobString(self) -> Optional[bytes]:
|
||||
try:
|
||||
conn = self._db._acquire_connection()
|
||||
cursor = conn.execute(self._sql, self._ordered_bind_args())
|
||||
row = cursor.fetchone()
|
||||
if not self._db._in_transaction:
|
||||
conn.commit()
|
||||
if row:
|
||||
v = row[0]
|
||||
if v is None:
|
||||
return None
|
||||
if isinstance(v, bytes):
|
||||
return v
|
||||
if isinstance(v, str):
|
||||
return v.encode("utf-8")
|
||||
return bytes(str(v), "utf-8")
|
||||
raise SQLiteDoneException("no results")
|
||||
except _sqlite3.DatabaseError as e:
|
||||
raise _map_sqlite_error(str(e), self._sql)
|
||||
|
||||
def execQuery(self) -> "SQLiteCursor":
|
||||
"""执行查询 SQL 并返回 SQLiteCursor。"""
|
||||
query = SQLiteQuery(self._db, self._sql)
|
||||
for idx, val in self._bindings.items():
|
||||
query.bind(idx, val)
|
||||
return SQLiteCursor(self._db, query)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# SQLiteQuery
|
||||
@@ -313,10 +349,17 @@ class SQLiteCursor(AbstractCursor):
|
||||
|
||||
def moveToPosition(self, position: int) -> bool:
|
||||
self._load()
|
||||
if position < -1 or position >= len(self._rows):
|
||||
count = len(self._rows)
|
||||
if position >= count:
|
||||
self._pos = count
|
||||
self._current_row = None
|
||||
return False
|
||||
if position < -1:
|
||||
self._pos = -1
|
||||
self._current_row = None
|
||||
return False
|
||||
self._pos = position
|
||||
if 0 <= position < len(self._rows):
|
||||
if 0 <= position < count:
|
||||
self._current_row = self._rows[position]
|
||||
else:
|
||||
self._current_row = None
|
||||
@@ -499,6 +542,39 @@ def _map_sqlite_error(msg: str, sql: str = "") -> SQLiteException:
|
||||
return SQLiteException(msg)
|
||||
|
||||
|
||||
def _extract_value_pairs(values: Any) -> List[Tuple[str, Any]]:
|
||||
"""从 ContentValues / dict / 类似对象中提取 (列名, 值) 列表。
|
||||
|
||||
注意: 本实现中 ContentValues.valueSet() 返回的是键集合而非 (键, 值) 对,
|
||||
因此需要通过 get() 取值。
|
||||
"""
|
||||
if values is None:
|
||||
return []
|
||||
if isinstance(values, dict):
|
||||
return list(values.items())
|
||||
# ContentValues 内部使用 _values dict
|
||||
inner = getattr(values, "_values", None)
|
||||
if isinstance(inner, dict):
|
||||
return list(inner.items())
|
||||
# ContentValues.valueSet() 在本实现中返回键集合
|
||||
if hasattr(values, "valueSet") and hasattr(values, "get"):
|
||||
try:
|
||||
return [(str(k), values.get(k)) for k in values.valueSet()]
|
||||
except Exception:
|
||||
pass
|
||||
if hasattr(values, "keySet") and hasattr(values, "get"):
|
||||
try:
|
||||
return [(str(k), values.get(k)) for k in values.keySet()]
|
||||
except Exception:
|
||||
pass
|
||||
if hasattr(values, "items"):
|
||||
try:
|
||||
return [(str(k), v) for k, v in values.items()]
|
||||
except Exception:
|
||||
pass
|
||||
return []
|
||||
|
||||
|
||||
class SQLiteDatabase(SQLiteClosable):
|
||||
"""SQLiteDatabase — 数据库操作类"""
|
||||
|
||||
@@ -536,10 +612,10 @@ class SQLiteDatabase(SQLiteClosable):
|
||||
try:
|
||||
db._conn = _sqlite3.connect(path)
|
||||
db._conn.execute("PRAGMA journal_mode=WAL")
|
||||
conn_cursor = db._conn.execute("PRAGMA schema_version")
|
||||
conn_cursor = db._conn.execute("PRAGMA user_version")
|
||||
row = conn_cursor.fetchone()
|
||||
if row:
|
||||
db._version = row[0]
|
||||
db._version = int(row[0])
|
||||
except _sqlite3.DatabaseError as e:
|
||||
raise _map_sqlite_error(str(e))
|
||||
SQLiteDatabase._db_registry[path] = db
|
||||
@@ -610,23 +686,37 @@ class SQLiteDatabase(SQLiteClosable):
|
||||
self._ensure_open()
|
||||
if values is None:
|
||||
return -1
|
||||
cols = []
|
||||
vals = []
|
||||
if hasattr(values, "valueSet"):
|
||||
items = list(values.valueSet())
|
||||
elif hasattr(values, "key_set"):
|
||||
items = [(k, getattr(values, f"get{k[0].upper()}{k[1:]}", lambda: None)())
|
||||
for k in values.key_set()]
|
||||
elif isinstance(values, dict):
|
||||
items = list(values.items())
|
||||
else:
|
||||
return -1
|
||||
for k, v in items:
|
||||
cols.append(str(k))
|
||||
vals.append(v)
|
||||
if len(cols) == 0:
|
||||
items = _extract_value_pairs(values)
|
||||
if not items:
|
||||
cols = ["_id"]
|
||||
vals = [None]
|
||||
vals: List[Any] = [None]
|
||||
else:
|
||||
cols = [str(k) for k, _ in items]
|
||||
vals = [v for _, v in items]
|
||||
col_str = ", ".join(cols)
|
||||
placeholders = ", ".join(["?"] * len(vals))
|
||||
sql = f"INSERT INTO {table} ({col_str}) VALUES ({placeholders})"
|
||||
try:
|
||||
conn = self._acquire_connection()
|
||||
cursor = conn.execute(sql, vals)
|
||||
if not self._in_transaction:
|
||||
conn.commit()
|
||||
return cursor.lastrowid
|
||||
except _sqlite3.DatabaseError:
|
||||
return -1
|
||||
|
||||
def insertOrThrow(self, table: str, null_column_hack: Optional[str],
|
||||
values: Any) -> int:
|
||||
self._ensure_open()
|
||||
if values is None:
|
||||
raise SQLiteException("null values not allowed")
|
||||
items = _extract_value_pairs(values)
|
||||
if not items:
|
||||
cols = ["_id"]
|
||||
vals: List[Any] = [None]
|
||||
else:
|
||||
cols = [str(k) for k, _ in items]
|
||||
vals = [v for _, v in items]
|
||||
col_str = ", ".join(cols)
|
||||
placeholders = ", ".join(["?"] * len(vals))
|
||||
sql = f"INSERT INTO {table} ({col_str}) VALUES ({placeholders})"
|
||||
@@ -639,23 +729,41 @@ class SQLiteDatabase(SQLiteClosable):
|
||||
except _sqlite3.DatabaseError as e:
|
||||
raise _map_sqlite_error(str(e), sql)
|
||||
|
||||
def replace(self, table: str, null_column_hack: Optional[str],
|
||||
values: Any) -> int:
|
||||
self._ensure_open()
|
||||
if values is None:
|
||||
return -1
|
||||
items = _extract_value_pairs(values)
|
||||
if not items:
|
||||
cols = ["_id"]
|
||||
vals: List[Any] = [None]
|
||||
else:
|
||||
cols = [str(k) for k, _ in items]
|
||||
vals = [v for _, v in items]
|
||||
col_str = ", ".join(cols)
|
||||
placeholders = ", ".join(["?"] * len(vals))
|
||||
sql = f"INSERT OR REPLACE INTO {table} ({col_str}) VALUES ({placeholders})"
|
||||
try:
|
||||
conn = self._acquire_connection()
|
||||
cursor = conn.execute(sql, vals)
|
||||
if not self._in_transaction:
|
||||
conn.commit()
|
||||
return cursor.lastrowid
|
||||
except _sqlite3.DatabaseError:
|
||||
return -1
|
||||
|
||||
def update(self, table: str, values: Any,
|
||||
where_clause: Optional[str] = None,
|
||||
where_args: Optional[List[Any]] = None) -> int:
|
||||
self._ensure_open()
|
||||
if values is None:
|
||||
return 0
|
||||
set_pairs = []
|
||||
vals = []
|
||||
if isinstance(values, dict):
|
||||
items = list(values.items())
|
||||
elif hasattr(values, "valueSet"):
|
||||
items = list(values.valueSet())
|
||||
else:
|
||||
items = _extract_value_pairs(values)
|
||||
if not items:
|
||||
return 0
|
||||
for k, v in items:
|
||||
set_pairs.append(f"{k} = ?")
|
||||
vals.append(v)
|
||||
set_pairs = [f"{k} = ?" for k, _ in items]
|
||||
vals: List[Any] = [v for _, v in items]
|
||||
set_str = ", ".join(set_pairs)
|
||||
sql = f"UPDATE {table} SET {set_str}"
|
||||
if where_clause:
|
||||
@@ -748,13 +856,24 @@ class SQLiteDatabase(SQLiteClosable):
|
||||
return self._path
|
||||
|
||||
def getVersion(self) -> int:
|
||||
if self._conn:
|
||||
try:
|
||||
cursor = self._conn.execute("PRAGMA user_version")
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
self._version = int(row[0])
|
||||
return self._version
|
||||
except _sqlite3.DatabaseError:
|
||||
pass
|
||||
return self._version
|
||||
|
||||
def setVersion(self, version: int):
|
||||
self._version = version
|
||||
self._version = int(version)
|
||||
if self._conn:
|
||||
try:
|
||||
self._conn.execute(f"PRAGMA schema_version = {version}")
|
||||
self._conn.execute(f"PRAGMA user_version = {int(version)}")
|
||||
if not self._in_transaction:
|
||||
self._conn.commit()
|
||||
except _sqlite3.DatabaseError:
|
||||
pass
|
||||
|
||||
@@ -784,20 +903,54 @@ class SQLiteDatabase(SQLiteClosable):
|
||||
return self._enable_wal
|
||||
|
||||
def getMaximumSize(self) -> int:
|
||||
if self._conn:
|
||||
try:
|
||||
page_size = self.getPageSize()
|
||||
if page_size <= 0:
|
||||
page_size = 4096
|
||||
cursor = self._conn.execute("PRAGMA max_page_count")
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
self._max_size = int(row[0]) * page_size
|
||||
return self._max_size
|
||||
except _sqlite3.DatabaseError:
|
||||
pass
|
||||
return self._max_size
|
||||
|
||||
def getPageSize(self) -> int:
|
||||
if self._conn:
|
||||
try:
|
||||
cursor = self._conn.execute("PRAGMA page_size")
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
self._page_size = int(row[0])
|
||||
return self._page_size
|
||||
except _sqlite3.DatabaseError:
|
||||
pass
|
||||
return self._page_size
|
||||
|
||||
def setMaximumSize(self, size: int) -> int:
|
||||
self._max_size = size
|
||||
return size
|
||||
|
||||
def setPageSize(self, size: int):
|
||||
self._page_size = size
|
||||
self._max_size = int(size)
|
||||
if self._conn:
|
||||
try:
|
||||
self._conn.execute(f"PRAGMA page_size = {size}")
|
||||
page_size = self.getPageSize()
|
||||
if page_size <= 0:
|
||||
page_size = 4096
|
||||
max_pages = max(1, int(size) // page_size)
|
||||
cursor = self._conn.execute(f"PRAGMA max_page_count = {max_pages}")
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
self._max_size = int(row[0]) * page_size
|
||||
return self._max_size
|
||||
except _sqlite3.DatabaseError:
|
||||
pass
|
||||
return self._max_size
|
||||
|
||||
def setPageSize(self, size: int):
|
||||
self._page_size = int(size)
|
||||
if self._conn:
|
||||
try:
|
||||
self._conn.execute(f"PRAGMA page_size = {int(size)}")
|
||||
except _sqlite3.DatabaseError:
|
||||
pass
|
||||
|
||||
|
||||
Reference in new issue
Block a user