refactor: split th_2_core.js into 12 modules, rename all files to 2-digit numbering

- Split th_2_core.js (4715 lines, 177KB) into:
  th_02_core.js, th_03_icon.js, th_04_theme.js, th_05_persistence.js,
  th_06_icon_parser.js, th_07_shortcut.js, th_08_content.js,
  th_09_animation.js, th_10_shell.js, th_11_action.js,
  th_12_rebuild.js, th_13_panel_ui.js
- Rename existing: th_1_base→th_01_base, th_3_panels→th_14_panels,
  th_4_extra→th_15_extra, th_5_entry→th_16_entry
- Update ToolHub.js MODULE_MANIFEST, modules array, and critical module check
This commit is contained in:
root
2026-04-20 11:53:13 +08:00
parent c64d4c336b
commit c7e9b92322
18 changed files with 4747 additions and 4722 deletions

209
code/th_08_content.js Normal file
View File

@@ -0,0 +1,209 @@
// @version 1.0.0
// =======================【Content解析 settings URI】======================
// 这段代码的主要内容/用途:识别 content://settings/(system|secure|global)/KEY 并用 Settings.* get/put 更稳
FloatBallAppWM.prototype.parseSettingsUri = function(uriStr) {
try {
var s = String(uriStr || "");
if (s.indexOf("content://settings/") !== 0) return null;
// content://settings/system/accelerometer_rotation
var rest = s.substring("content://settings/".length);
var parts = rest.split("/");
if (!parts || parts.length < 1) return null;
var table = String(parts[0] || "");
var key = "";
if (parts.length >= 2) key = String(parts[1] || "");
if (table !== "system" && table !== "secure" && table !== "global") return null;
return { table: table, key: key };
} catch (e) { return null; }
};
FloatBallAppWM.prototype.settingsGetStringByTable = function(table, key) {
try {
var cr = context.getContentResolver();
if (table === "system") return android.provider.Settings.System.getString(cr, String(key));
if (table === "secure") return android.provider.Settings.Secure.getString(cr, String(key));
if (table === "global") return android.provider.Settings.Global.getString(cr, String(key));
return null;
} catch (e) { return null; }
};
FloatBallAppWM.prototype.settingsPutStringByTable = function(table, key, value) {
try {
var cr = context.getContentResolver();
if (table === "system") return android.provider.Settings.System.putString(cr, String(key), String(value));
if (table === "secure") return android.provider.Settings.Secure.putString(cr, String(key), String(value));
if (table === "global") return android.provider.Settings.Global.putString(cr, String(key), String(value));
return false;
} catch (e) { return false; }
};
// =======================【Content通用 query】======================
// 这段代码的主要内容/用途ContentResolver.query 并把 Cursor 转成文本(用于查看器面板)
FloatBallAppWM.prototype.contentQueryToText = function(uriStr, projection, selection, selectionArgs, sortOrder, maxRows) {
var out = { ok: false, uri: String(uriStr || ""), rows: 0, text: "", err: "" };
var cr = null;
var cur = null;
try {
cr = context.getContentResolver();
var uri = android.net.Uri.parse(String(uriStr));
var projArr = null;
if (projection && projection.length) {
projArr = java.lang.reflect.Array.newInstance(java.lang.String, projection.length);
var i0;
for (i0 = 0; i0 < projection.length; i0++) projArr[i0] = String(projection[i0]);
}
var sel = (selection === undefined || selection === null) ? null : String(selection);
var selArgsArr = null;
if (selectionArgs && selectionArgs.length) {
selArgsArr = java.lang.reflect.Array.newInstance(java.lang.String, selectionArgs.length);
var i1;
for (i1 = 0; i1 < selectionArgs.length; i1++) selArgsArr[i1] = String(selectionArgs[i1]);
}
var so = (sortOrder === undefined || sortOrder === null) ? null : String(sortOrder);
cur = cr.query(uri, projArr, sel, selArgsArr, so);
if (!cur) {
out.err = "query return null cursor";
return out;
}
var colCount = cur.getColumnCount();
var cols = [];
var ci;
for (ci = 0; ci < colCount; ci++) cols.push(String(cur.getColumnName(ci)));
var sb = [];
sb.push("URI: " + String(uriStr));
sb.push("Columns(" + String(colCount) + "): " + cols.join(", "));
sb.push("");
var limit = Math.max(1, Math.floor(Number(maxRows || this.config.CONTENT_MAX_ROWS || 20)));
var row = 0;
while (cur.moveToNext()) {
row++;
sb.push("#" + String(row));
var cj;
for (cj = 0; cj < colCount; cj++) {
var v = "";
try {
if (cur.isNull(cj)) v = "null";
else v = String(cur.getString(cj));
} catch (eV) {
try { v = String(cur.getLong(cj)); } catch (eV2) { v = "<??>"; }
}
sb.push(" " + cols[cj] + " = " + v);
}
sb.push("");
if (row >= limit) break;
}
out.ok = true;
out.rows = row;
out.text = sb.join("\n");
return out;
} catch (e) {
out.err = String(e);
return out;
} finally {
try { if (cur) cur.close(); } catch (eC) {}
}
};
// =======================【Content统一入口】======================
// 这段代码的主要内容/用途:处理按钮里的 type:"content"
FloatBallAppWM.prototype.execContentAction = function(btn) {
var mode = btn.mode ? String(btn.mode) : ((btn.value !== undefined && btn.value !== null) ? "put" : "get");
var uri = btn.uri ? String(btn.uri) : "";
if (!uri) return { ok: false, err: "missing uri" };
// settings uri 优先走 Settings API
var su = this.parseSettingsUri(uri);
if (mode === "get") {
if (su && su.key) {
var v = this.settingsGetStringByTable(su.table, su.key);
return { ok: true, mode: "get", kind: "settings", table: su.table, key: su.key, value: (v === null ? "null" : String(v)) };
}
// 非 settings尝试 query 一行
var q1 = this.contentQueryToText(uri, btn.projection, btn.selection, btn.selectionArgs, btn.sortOrder, 1);
if (!q1.ok) return { ok: false, mode: "get", kind: "query", err: q1.err };
return { ok: true, mode: "get", kind: "query", text: q1.text, rows: q1.rows };
}
if (mode === "put") {
var val = (btn.value === undefined || btn.value === null) ? "" : String(btn.value);
if (su && su.key) {
var ok = this.settingsPutStringByTable(su.table, su.key, val);
return { ok: !!ok, mode: "put", kind: "settings", table: su.table, key: su.key, value: val };
}
// 非 settings尽力走 update(ContentValues)
try {
var cr = context.getContentResolver();
var u = android.net.Uri.parse(uri);
var cv = new android.content.ContentValues();
// 支持 btn.values = {col1: "...", col2: "..."};否则写 value 列
if (btn.values) {
var k;
for (k in btn.values) {
if (!btn.values.hasOwnProperty(k)) continue;
cv.put(String(k), String(btn.values[k]));
}
} else {
cv.put("value", val);
}
var where = (btn.selection === undefined || btn.selection === null) ? null : String(btn.selection);
var whereArgs = null;
if (btn.selectionArgs && btn.selectionArgs.length) {
whereArgs = java.lang.reflect.Array.newInstance(java.lang.String, btn.selectionArgs.length);
var i2;
for (i2 = 0; i2 < btn.selectionArgs.length; i2++) whereArgs[i2] = String(btn.selectionArgs[i2]);
}
var n = cr.update(u, cv, where, whereArgs);
return { ok: true, mode: "put", kind: "update", updated: Number(n) };
} catch (eU) {
return { ok: false, mode: "put", kind: "update", err: String(eU) };
}
}
if (mode === "query") {
var maxRows = (btn.maxRows === undefined || btn.maxRows === null) ? this.config.CONTENT_MAX_ROWS : Number(btn.maxRows);
var q = this.contentQueryToText(uri, btn.projection, btn.selection, btn.selectionArgs, btn.sortOrder, maxRows);
if (!q.ok) return { ok: false, mode: "query", err: q.err };
return { ok: true, mode: "query", rows: q.rows, text: q.text };
}
if (mode === "view") {
try {
var it = new android.content.Intent(android.content.Intent.ACTION_VIEW);
it.setData(android.net.Uri.parse(uri));
it.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(it);
return { ok: true, mode: "view" };
} catch (eV) {
return { ok: false, mode: "view", err: String(eV) };
}
}
return { ok: false, err: "unknown mode=" + mode };
};
/* =======================
下面开始WM 动画、面板、触摸、启动、输出
======================= */