fix: 代码审查6项修复

- 689处空catch块补全日志
- eval远程代码增加SHA256校验
- 删除ToolHubLogger重复定义
- getParentFile()增加null保护
- 提取buildButtonEditorPanelView内通用工具函数到文件级
- 修复HandlerThread/ValueAnimator资源泄漏
This commit is contained in:
linshenjianlu
2026-04-21 07:38:53 +08:00
commit fc0d013ff2
18 changed files with 14124 additions and 0 deletions

800
code/th_04_theme.js Normal file
View File

@@ -0,0 +1,800 @@
// @version 1.0.0
// =======================【工具:屏幕/旋转】======================
FloatBallAppWM.prototype.getScreenSizePx = function() {
var m = new android.util.DisplayMetrics();
try { this.state.wm.getDefaultDisplay().getRealMetrics(m); } catch (e) { this.state.wm.getDefaultDisplay().getMetrics(m); }
return { w: m.widthPixels, h: m.heightPixels };
};
FloatBallAppWM.prototype.getRotation = function() { try { return this.state.wm.getDefaultDisplay().getRotation(); } catch(e) { safeLog(null, 'e', "catch " + String(e)); } return -1; };
// =======================【工具alpha/toast/vibrate】======================
FloatBallAppWM.prototype.withAlpha = function(colorInt, alpha01) { var a = Math.floor(Number(alpha01) * 255); return (colorInt & 0x00FFFFFF) | (a << 24); };
FloatBallAppWM.prototype.toast = function(msg) { try { android.widget.Toast.makeText(context, String(msg), 0).show(); } catch(e) { safeLog(null, 'e', "catch " + String(e)); } };
FloatBallAppWM.prototype.vibrateOnce = function(ms) {
if (!this.config.LONG_PRESS_HAPTIC_ENABLE) return;
try {
var vib = context.getSystemService(android.content.Context.VIBRATOR_SERVICE);
if (!vib) return;
var dur = Math.max(1, Math.floor(ms));
if (android.os.Build.VERSION.SDK_INT >= 26) {
var ve = android.os.VibrationEffect.createOneShot(dur, android.os.VibrationEffect.DEFAULT_AMPLITUDE);
vib.vibrate(ve);
} else {
vib.vibrate(dur);
}
} catch(e) { safeLog(null, 'e', "catch " + String(e)); }
};
// =======================【工具UI样式辅助】======================
FloatBallAppWM.prototype.ui = {
// 基础颜色
colors: {
// 以下为默认回退值,实例化时会被 refreshMonetColors() 覆盖为系统莫奈色
primary: android.graphics.Color.parseColor("#005BC0"),
primaryDark: android.graphics.Color.parseColor("#041E49"),
accent: android.graphics.Color.parseColor("#00639B"),
danger: android.graphics.Color.parseColor("#BA1A1A"),
success: android.graphics.Color.parseColor("#15803d"),
warning: android.graphics.Color.parseColor("#b45309"),
bgLight: android.graphics.Color.parseColor("#F8F9FA"),
bgDark: android.graphics.Color.parseColor("#131314"),
cardLight: android.graphics.Color.parseColor("#E1E3E1"),
cardDark: android.graphics.Color.parseColor("#49454F"),
textPriLight: android.graphics.Color.parseColor("#1F1F1F"),
textPriDark: android.graphics.Color.parseColor("#E3E3E3"),
textSecLight: android.graphics.Color.parseColor("#5F6368"),
textSecDark: android.graphics.Color.parseColor("#C4C7C5"),
dividerLight: android.graphics.Color.parseColor("#747775"),
dividerDark: android.graphics.Color.parseColor("#8E918F"),
inputBgLight: android.graphics.Color.parseColor("#F8F9FA"),
inputBgDark: android.graphics.Color.parseColor("#131314"),
// Monet 扩展字段(供面板直接使用)
_monetSurface: android.graphics.Color.parseColor("#F8F9FA"),
_monetOnSurface: android.graphics.Color.parseColor("#1F1F1F"),
_monetOutline: android.graphics.Color.parseColor("#747775"),
_monetOnPrimary: android.graphics.Color.parseColor("#FFFFFF"),
_monetPrimaryContainer: android.graphics.Color.parseColor("#D3E3FD"),
_monetOnPrimaryContainer: android.graphics.Color.parseColor("#041E49"),
_monetSecondary: android.graphics.Color.parseColor("#00639B"),
_monetTertiary: android.graphics.Color.parseColor("#5C5891")
},
// 创建圆角背景 (Solid)
createRoundDrawable: function(color, radiusPx) {
var d = new android.graphics.drawable.GradientDrawable();
d.setShape(android.graphics.drawable.GradientDrawable.RECTANGLE);
d.setColor(color);
d.setCornerRadius(radiusPx);
return d;
},
// 创建圆角描边背景 (Stroke)
createStrokeDrawable: function(fillColor, strokeColor, strokeWidthPx, radiusPx) {
var d = new android.graphics.drawable.GradientDrawable();
d.setShape(android.graphics.drawable.GradientDrawable.RECTANGLE);
if (fillColor) d.setColor(fillColor);
d.setCornerRadius(radiusPx);
d.setStroke(strokeWidthPx, strokeColor);
return d;
},
// 创建按压反馈背景 (StateList)
createRippleDrawable: function(normalColor, pressedColor, radiusPx) {
var sd = new android.graphics.drawable.StateListDrawable();
var p = this.createRoundDrawable(pressedColor, radiusPx);
var n = this.createRoundDrawable(normalColor, radiusPx);
sd.addState([android.R.attr.state_pressed], p);
sd.addState([], n);
return sd;
},
// 创建纯色按压反馈 (StateList) - 用于透明背景按钮
createTransparentRippleDrawable: function(pressedColor, radiusPx) {
var sd = new android.graphics.drawable.StateListDrawable();
var p = this.createRoundDrawable(pressedColor, radiusPx);
var n = new android.graphics.drawable.ColorDrawable(android.graphics.Color.TRANSPARENT);
sd.addState([android.R.attr.state_pressed], p);
sd.addState([], n);
return sd;
},
// 辅助:创建扁平按钮
createFlatButton: function(app, txt, txtColor, onClick) {
var btn = new android.widget.TextView(context);
btn.setText(txt);
btn.setTextColor(txtColor);
btn.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 14);
btn.setPadding(app.dp(12), app.dp(6), app.dp(12), app.dp(6));
btn.setGravity(android.view.Gravity.CENTER);
// use divider color or just low alpha text color for ripple
var rippleColor = app.withAlpha ? app.withAlpha(txtColor, 0.1) : 0x22888888;
btn.setBackground(this.createTransparentRippleDrawable(rippleColor, app.dp(8)));
btn.setOnClickListener(new android.view.View.OnClickListener({
onClick: function(v) { app.touchActivity(); app.guardClick("ui_btn", INTERACTION_CONSTANTS.CLICK_COOLDOWN_MS, function(){ if(onClick) onClick(v); }); }
}));
return btn;
},
// 辅助:创建实心按钮
createSolidButton: function(app, txt, bgColor, txtColor, onClick) {
var btn = new android.widget.TextView(context);
btn.setText(txt);
btn.setTextColor(txtColor);
btn.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 14);
btn.setTypeface(null, android.graphics.Typeface.BOLD);
btn.setPadding(app.dp(16), app.dp(8), app.dp(16), app.dp(8));
btn.setGravity(android.view.Gravity.CENTER);
var pressedColor = app.withAlpha ? app.withAlpha(bgColor, 0.8) : bgColor;
btn.setBackground(this.createRippleDrawable(bgColor, pressedColor, app.dp(24)));
try { btn.setElevation(app.dp(2)); } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
btn.setOnClickListener(new android.view.View.OnClickListener({
onClick: function(v) { app.touchActivity(); app.guardClick("ui_btn", INTERACTION_CONSTANTS.CLICK_COOLDOWN_MS, function(){ if(onClick) onClick(v); }); }
}));
return btn;
},
// 辅助:创建带标签的输入组(支持粘贴)
createInputGroup: function(app, label, initVal, isMultiLine, hint) {
var box = new android.widget.LinearLayout(context);
box.setOrientation(android.widget.LinearLayout.VERTICAL);
box.setPadding(0, 0, 0, app.dp(12));
var topLine = new android.widget.LinearLayout(context);
topLine.setOrientation(android.widget.LinearLayout.HORIZONTAL);
topLine.setGravity(android.view.Gravity.CENTER_VERTICAL);
box.addView(topLine);
var lb = new android.widget.TextView(context);
lb.setText(label);
lb.setTextColor(this.colors.textSecLight); // 默认用浅色主题副文本色,外部可覆盖
try { if (app.isDarkTheme && app.isDarkTheme()) lb.setTextColor(this.colors.textSecDark); } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
lb.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12);
var lpLb = new android.widget.LinearLayout.LayoutParams(0, -2);
lpLb.weight = 1;
topLine.addView(lb, lpLb);
var et = new android.widget.EditText(context);
et.setText(initVal ? String(initVal) : "");
et.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 14);
et.setTextColor(this.colors.textPriLight);
try { if (app.isDarkTheme && app.isDarkTheme()) et.setTextColor(this.colors.textPriDark); } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
// 输入框背景优化
var strokeColor = this.colors.dividerLight;
try { if (app.isDarkTheme && app.isDarkTheme()) strokeColor = this.colors.dividerDark; } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
var bg = this.createStrokeDrawable(this.colors.inputBgLight, strokeColor, app.dp(1), app.dp(8));
try { if (app.isDarkTheme && app.isDarkTheme()) bg = this.createStrokeDrawable(this.colors.inputBgDark, strokeColor, app.dp(1), app.dp(8)); } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
et.setBackground(bg);
et.setPadding(app.dp(8), app.dp(8), app.dp(8), app.dp(8));
if (hint) et.setHint(hint);
if (isMultiLine) {
et.setSingleLine(false);
et.setMaxLines(4);
} else {
et.setSingleLine(true);
}
// 粘贴功能
var pasteBtn = this.createFlatButton(app, "粘贴", this.colors.accent, function() {
try {
var cb = context.getSystemService(android.content.Context.CLIPBOARD_SERVICE);
if (cb.hasPrimaryClip()) {
var item = cb.getPrimaryClip().getItemAt(0);
if (item) {
var txt = item.getText();
if (txt) {
var st = String(txt);
var old = String(et.getText());
if (old.length > 0) et.setText(old + st);
else et.setText(st);
et.setSelection(et.getText().length());
}
}
} else {
app.toast("剪贴板为空");
}
} catch (eP) {
app.toast("粘贴失败: " + eP);
}
});
// 调整粘贴按钮样式使其更紧凑
pasteBtn.setPadding(app.dp(8), app.dp(2), app.dp(8), app.dp(2));
pasteBtn.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12);
topLine.addView(pasteBtn);
box.addView(et);
// 错误提示
var errTv = new android.widget.TextView(context);
errTv.setTextColor(this.colors.danger);
errTv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 10);
errTv.setVisibility(android.view.View.GONE);
box.addView(errTv);
var self = this;
return {
view: box,
input: et,
getValue: function() { return String(et.getText()); },
setError: function(msg) {
if (msg) {
errTv.setText(msg);
errTv.setVisibility(android.view.View.VISIBLE);
et.setBackground(self.createRoundDrawable(app.withAlpha(self.colors.danger, 0.2), app.dp(4)));
} else {
errTv.setVisibility(android.view.View.GONE);
var strokeColor = self.colors.dividerLight;
try { if (app.isDarkTheme && app.isDarkTheme()) strokeColor = self.colors.dividerDark; } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
var normalBg = self.createStrokeDrawable(self.colors.inputBgLight, strokeColor, app.dp(1), app.dp(8));
try { if (app.isDarkTheme && app.isDarkTheme()) normalBg = self.createStrokeDrawable(self.colors.inputBgDark, strokeColor, app.dp(1), app.dp(8)); } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
et.setBackground(normalBg);
}
}
};
},
// 辅助:创建标准面板容器
createStyledPanel: function(app, paddingDp) {
var isDark = app.isDarkTheme();
var bgColor = isDark ? this.colors.bgDark : this.colors.bgLight;
var panel = new android.widget.LinearLayout(context);
panel.setOrientation(android.widget.LinearLayout.VERTICAL);
var bgDr = new android.graphics.drawable.GradientDrawable();
bgDr.setColor(bgColor);
bgDr.setCornerRadius(app.dp(16));
panel.setBackground(bgDr);
try { panel.setElevation(app.dp(8)); } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
var p = (paddingDp !== undefined) ? app.dp(paddingDp) : app.dp(16);
panel.setPadding(p, p, p, p);
return panel;
},
// 辅助:创建标准标题栏容器
createStyledHeader: function(app, paddingBottomDp) {
var header = new android.widget.LinearLayout(context);
header.setOrientation(android.widget.LinearLayout.HORIZONTAL);
header.setGravity(android.view.Gravity.CENTER_VERTICAL);
var pb = (paddingBottomDp !== undefined) ? app.dp(paddingBottomDp) : app.dp(8);
header.setPadding(0, 0, 0, pb);
return header;
},
// 辅助:创建占位符(撑开空间)
createSpacer: function(app) {
var dummy = new android.view.View(context);
var dummyLp = new android.widget.LinearLayout.LayoutParams(0, 1);
dummyLp.weight = 1;
dummy.setLayoutParams(dummyLp);
return dummy;
}
};
// =======================【工具:主题/类莫奈颜色】======================
// # 主题调试日志工具(仅打印,不改变逻辑)
function _th_hex(c) {
try {
var Integer = Packages.java.lang.Integer;
var s = Integer.toHexString(c);
while (s.length < 8) s = "0" + s;
return "0x" + s;
} catch (e) {
try { return String(c); } catch (e2) { return "<?>"; }
}
}
function _th_argb(c) {
try {
var Color = Packages.android.graphics.Color;
var ci = Math.floor(Number(c));
if (isNaN(ci)) return "NaN";
return "a=" + Color.alpha(ci) + " r=" + Color.red(ci) + " g=" + Color.green(ci) + " b=" + Color.blue(ci);
} catch (e) {
return "argb_err";
}
}
function _th_log(L, level, msg) {
try {
if (!L) return;
if (level === "e" && L.e) { L.e(msg); return; }
if (level === "w" && L.w) { L.w(msg); return; }
if (level === "i" && L.i) { L.i(msg); return; }
if (L.d) { L.d(msg); return; }
} catch(e) { safeLog(null, 'e', "catch " + String(e)); }
}
// =======================【莫奈动态取色工具】======================
var MonetColorProvider = {
_cacheLight: null,
_cacheDark: null,
_getResColor: function(resName, fallbackHex) {
try {
var res = android.content.res.Resources.getSystem();
var id = res.getIdentifier(resName, "color", "android");
if (id > 0) return res.getColor(id, null);
} catch(e) { safeLog(null, 'e', "catch " + String(e)); }
try { return android.graphics.Color.parseColor(fallbackHex); } catch (e) { return 0; }
},
_getFallbackLight: function() {
return {
// 使用更接近 AOSP Monet 标准值的 fallback日间 primary 更饱和、对比度更高
primary: android.graphics.Color.parseColor("#005BC0"),
onPrimary: android.graphics.Color.parseColor("#FFFFFF"),
primaryContainer: android.graphics.Color.parseColor("#D3E3FD"),
onPrimaryContainer: android.graphics.Color.parseColor("#041E49"),
secondary: android.graphics.Color.parseColor("#00639B"),
secondaryContainer: android.graphics.Color.parseColor("#C2E4FF"),
tertiary: android.graphics.Color.parseColor("#5C5891"),
surface: android.graphics.Color.parseColor("#F8F9FA"),
onSurface: android.graphics.Color.parseColor("#1F1F1F"),
surfaceVariant: android.graphics.Color.parseColor("#E1E3E1"),
onSurfaceVariant: android.graphics.Color.parseColor("#5F6368"),
outline: android.graphics.Color.parseColor("#747775"),
outlineVariant: android.graphics.Color.parseColor("#C4C7C5"),
error: android.graphics.Color.parseColor("#BA1A1A"),
errorContainer: android.graphics.Color.parseColor("#F9DEDC"),
onErrorContainer: android.graphics.Color.parseColor("#410E0B")
};
},
_getFallbackDark: function() {
return {
primary: android.graphics.Color.parseColor("#A8C7FA"),
onPrimary: android.graphics.Color.parseColor("#062E6F"),
primaryContainer: android.graphics.Color.parseColor("#0842A0"),
onPrimaryContainer: android.graphics.Color.parseColor("#D3E3FD"),
secondary: android.graphics.Color.parseColor("#7FCFFF"),
secondaryContainer: android.graphics.Color.parseColor("#004A77"),
tertiary: android.graphics.Color.parseColor("#C2C5DD"),
surface: android.graphics.Color.parseColor("#131314"),
onSurface: android.graphics.Color.parseColor("#E3E3E3"),
surfaceVariant: android.graphics.Color.parseColor("#49454F"),
onSurfaceVariant: android.graphics.Color.parseColor("#C4C7C5"),
outline: android.graphics.Color.parseColor("#8E918F"),
outlineVariant: android.graphics.Color.parseColor("#49454F"),
error: android.graphics.Color.parseColor("#F2B8B5"),
errorContainer: android.graphics.Color.parseColor("#8C1D18"),
onErrorContainer: android.graphics.Color.parseColor("#F9DEDC")
};
},
_loadMonet: function(isDark) {
var c = isDark ? this._getFallbackDark() : this._getFallbackLight();
try {
var res = android.content.res.Resources.getSystem();
var map = isDark ? {
primary: "system_accent1_200",
onPrimary: "system_accent1_800",
primaryContainer: "system_accent1_700",
onPrimaryContainer: "system_accent1_100",
secondary: "system_accent2_200",
secondaryContainer: "system_accent2_700",
tertiary: "system_accent3_200",
surface: "system_neutral1_900",
onSurface: "system_neutral1_100",
surfaceVariant: "system_neutral2_700",
onSurfaceVariant: "system_neutral2_200",
outline: "system_neutral2_400",
outlineVariant: "system_neutral2_700",
error: "system_accent3_200",
errorContainer: "system_accent3_800",
onErrorContainer: "system_accent3_100"
} : {
primary: "system_accent1_600",
onPrimary: "system_accent1_0",
primaryContainer: "system_accent1_100",
onPrimaryContainer: "system_accent1_900",
secondary: "system_accent2_600",
secondaryContainer: "system_accent2_100",
tertiary: "system_accent3_600",
surface: "system_neutral1_10",
onSurface: "system_neutral1_900",
surfaceVariant: "system_neutral2_100",
onSurfaceVariant: "system_neutral2_700",
outline: "system_neutral2_500",
outlineVariant: "system_neutral2_200",
error: "system_accent3_600",
errorContainer: "system_accent3_100",
onErrorContainer: "system_accent3_900"
};
for (var name in map) {
try {
var id = res.getIdentifier(map[name], "color", "android");
if (id > 0) c[name] = res.getColor(id, null);
} catch(e) { safeLog(null, 'e', "catch " + String(e)); }
}
} catch(e) { safeLog(null, 'e', "catch " + String(e)); }
return c;
},
getColors: function(isDark) {
if (isDark) {
if (!this._cacheDark) this._cacheDark = this._loadMonet(true);
return this._cacheDark;
} else {
if (!this._cacheLight) this._cacheLight = this._loadMonet(false);
return this._cacheLight;
}
},
invalidate: function() {
this._cacheLight = null;
this._cacheDark = null;
}
};
// =======================【兼容兜底themeTextInt/themeBgInt】======================
// 这段代码的主要内容/用途:兼容旧代码或异步回调里误引用 themeTextInt/themeBgInt 导致 ReferenceError 崩溃。
// 说明:当前版本文字色应通过 getPanelTextColorInt(bgInt) 获取;这里仅作为"兜底全局变量",避免回调炸线程。
// 声明全局变量(避免 ReferenceError
var themeBgInt = 0;
var themeTextInt = 0;
// =======================【API 兼容性辅助函数】======================
// 这段代码的主要内容/用途:处理 Android API 级别差异,避免在旧版本上崩溃
/**
* 安全创建 UserHandle兼容 API 17 以下)
* @param {number} userId - 用户 ID
* @returns {android.os.UserHandle} UserHandle 对象或 null
*/
function createUserHandle(userId) {
try {
// UserHandle.of 在 API 17+ 可用
if (android.os.Build.VERSION.SDK_INT >= 17) {
return android.os.UserHandle.of(userId);
}
// API 17 以下返回当前用户句柄
return android.os.Process.myUserHandle();
} catch (e) {
return null;
}
}
/**
* 安全启动 Activity 跨用户(兼容 API 17 以下)
* @param {Context} ctx - Context
* @param {Intent} intent - Intent
* @param {number} userId - 用户 IDAPI 17+ 有效)
*/
function startActivityAsUserSafe(ctx, intent, userId) {
try {
if (android.os.Build.VERSION.SDK_INT >= 17 && userId !== 0) {
var uh = android.os.UserHandle.of(userId);
ctx.startActivityAsUser(intent, uh);
} else {
ctx.startActivity(intent);
}
} catch (e) {
// 降级到普通启动
try {
ctx.startActivity(intent);
} catch(e2) { safeLog(null, 'e', "catch " + String(e2)); }
}
}
FloatBallAppWM.prototype.isDarkTheme = function() {
// 0) 优先检查用户强制设置 (0=跟随系统, 1=白天, 2=黑夜)
var mode = 0;
try { mode = Math.floor(Number(this.config.THEME_MODE || 0)); } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
if (mode === 2) return true;
if (mode === 1) return false;
// mode === 0 (or others) -> Fallback to system detection
// 这段代码的主要内容/用途:更稳健地判断当前是否处于"夜间/暗色"模式(并打印调试日志)。
// 说明system_server 场景下 Configuration.uiMode 偶发不一致,因此再用 UiModeManager 兜底交叉验证。
var result = false;
var from = "unknown";
try {
// # 1) 优先用 Configuration最快
var uiMode = context.getResources().getConfiguration().uiMode;
var nightMask = (uiMode & android.content.res.Configuration.UI_MODE_NIGHT_MASK);
if (nightMask === android.content.res.Configuration.UI_MODE_NIGHT_YES) { result = true; from = "Configuration(UI_MODE_NIGHT_YES)"; }
else if (nightMask === android.content.res.Configuration.UI_MODE_NIGHT_NO) { result = false; from = "Configuration(UI_MODE_NIGHT_NO)"; }
} catch(e1) { safeLog(null, 'e', "catch " + String(e1)); }
if (from === "unknown") {
try {
// # 2) 再用 UiModeManager更"系统态"
var um = context.getSystemService(android.content.Context.UI_MODE_SERVICE);
if (um) {
var nm = um.getNightMode();
if (nm === android.app.UiModeManager.MODE_NIGHT_YES) { result = true; from = "UiModeManager(MODE_NIGHT_YES)"; }
else if (nm === android.app.UiModeManager.MODE_NIGHT_NO) { result = false; from = "UiModeManager(MODE_NIGHT_NO)"; }
else { from = "UiModeManager(mode=" + String(nm) + ")"; }
}
} catch(e2) { safeLog(null, 'e', "catch " + String(e2)); }
}
// # 3) 实在判断不了,就按"非暗色"处理,避免自动主题背景黑成一片
if (from === "unknown") { result = false; from = "fallback(false)"; }
// 仅在状态改变时打印日志,避免刷屏
var logKey = String(result) + "|" + from + "|" + mode;
if (this._lastDarkThemeLog !== logKey) {
this._lastDarkThemeLog = logKey;
try { _th_log(this.L, "d", "[theme] isDarkTheme=" + String(result) + " via=" + from + " mode=" + mode); } catch(e3) { safeLog(null, 'e', "catch " + String(e3)); }
}
// # 主题切换时刷新莫奈配色(传入 result 避免递归)
// 注:构造函数中会初始化,这里只在构造完成后的切换时触发
if (this._lastDarkResult !== undefined && this._lastDarkResult !== result) {
this._lastDarkResult = result;
try { this.refreshMonetColors(result); } catch(eM) { safeLog(null, 'e', "catch " + String(eM)); }
} else if (this._lastDarkResult === undefined) {
this._lastDarkResult = result;
}
return result;
};
FloatBallAppWM.prototype.refreshMonetColors = function(forceDark) {
try {
var isDark = (forceDark !== undefined) ? forceDark : this.isDarkTheme();
var m = MonetColorProvider.getColors(isDark);
var ml = MonetColorProvider.getColors(false);
var md = MonetColorProvider.getColors(true);
var c = this.ui.colors;
// 浅色配色
c.bgLight = ml.surface;
c.cardLight = ml.surfaceVariant;
c.textPriLight = ml.onSurface;
c.textSecLight = ml.onSurfaceVariant;
c.dividerLight = ml.outline;
c.inputBgLight = ml.surface;
// 深色配色
c.bgDark = md.surface;
c.cardDark = md.surfaceVariant;
c.textPriDark = md.onSurface;
c.textSecDark = md.onSurfaceVariant;
c.dividerDark = md.outline;
c.inputBgDark = md.surface;
// 当前主题配色(随主题切换)
c.primary = m.primary;
// primaryDark 修正为 onPrimaryContainer日间为深蓝(#041E49),夜间为浅蓝(#D3E3FD),符合"深色变体"语义
c.primaryDark = m.onPrimaryContainer;
c.accent = m.secondary;
c.danger = m.error;
// success/warning 优化对比度:日间更深确保可见,夜间保持适度亮度不刺眼
c.success = isDark ? android.graphics.Color.parseColor("#4ade80") : android.graphics.Color.parseColor("#15803d");
c.warning = isDark ? android.graphics.Color.parseColor("#fbbf24") : android.graphics.Color.parseColor("#b45309");
// 扩展:完整 Monet 语义字段(供面板方法直接使用)
c._monetSurface = m.surface;
c._monetOnSurface = m.onSurface;
c._monetOutline = m.outline;
c._monetOnPrimary = m.onPrimary;
c._monetPrimaryContainer = m.primaryContainer;
c._monetOnPrimaryContainer = m.onPrimaryContainer;
c._monetSecondary = m.secondary;
c._monetTertiary = m.tertiary;
try { _th_log(this.L, "d", "[monet] refreshed isDark=" + isDark + " primary=" + _th_hex(c.primary) + " primaryDark=" + _th_hex(c.primaryDark) + " accent=" + _th_hex(c.accent)); } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
} catch (e) {
try { _th_log(this.L, "e", "[monet] refresh err=" + String(e)); } catch(e2) { safeLog(null, 'e', "catch " + String(e2)); }
}
};
FloatBallAppWM.prototype.getMonetAccentForBall = function() {
// 这段代码的主要内容/用途尝试读取系统动态强调色Monet/accent失败则使用兜底颜色并打印命中信息。
// 优化点:
// 1) 日间使用 500/600 档(更深、对比度更高),夜间使用 400/300 档(柔和、不刺眼)
// 2) 移除 system_neutral1_* 中性灰色(不是强调色)
var res = context.getResources();
var dark = this.isDarkTheme();
var names = dark
? ["system_accent1_400", "system_accent1_300", "system_accent2_400"]
: ["system_accent1_500", "system_accent1_600", "system_accent2_500"];
var i;
for (i = 0; i < names.length; i++) {
try {
var id = res.getIdentifier(names[i], "color", "android");
if (id > 0) {
var c = res.getColor(id, null);
var logKey = "hit|" + names[i] + "|" + c;
if (this._lastAccentLog !== logKey) {
this._lastAccentLog = logKey;
try { _th_log(this.L, "d", "[theme] hit accent=" + names[i] + " id=" + String(id) + " c=" + _th_hex(c) + " " + _th_argb(c)); } catch(eL0) { safeLog(null, 'e', "catch " + String(eL0)); }
}
return c;
}
} catch (e) {
try { _th_log(this.L, "w", "[theme] err accent=" + names[i] + " e=" + String(e)); } catch(eL2) { safeLog(null, 'e', "catch " + String(eL2)); }
}
}
var fbHex = dark
? (this.config.BALL_FALLBACK_DARK || CONST_BALL_FALLBACK_DARK)
: (this.config.BALL_FALLBACK_LIGHT || CONST_BALL_FALLBACK_LIGHT);
var fb = android.graphics.Color.parseColor(fbHex);
var logKeyFb = "miss_all|" + fb;
if (this._lastAccentLog !== logKeyFb) {
this._lastAccentLog = logKeyFb;
try { _th_log(this.L, "w", "[theme] accent miss all, fallback=" + _th_hex(fb) + " " + _th_argb(fb)); } catch(eL3) { safeLog(null, 'e', "catch " + String(eL3)); }
}
return fb;
};
FloatBallAppWM.prototype.updateBallContentBackground = function(contentView) {
try {
var ballColor = this.getMonetAccentForBall();
var dark = this.isDarkTheme();
var alpha01 = dark ? this.config.BALL_RIPPLE_ALPHA_DARK : this.config.BALL_RIPPLE_ALPHA_LIGHT;
var rippleColor = this.withAlpha(ballColor, alpha01);
// # 自定义 PNG/APP 模式下:背景透明
var fillColor = ballColor;
var _usedKind = "none";
try { _usedKind = this.state.usedIconKind || "none"; } catch(eK) { safeLog(null, 'e', "catch " + String(eK)); }
try {
var _pngModeBg = Number(this.config.BALL_PNG_MODE || 0);
if ((_pngModeBg === 1 && _usedKind === "file") || _usedKind === "app") {
fillColor = android.graphics.Color.TRANSPARENT;
}
} catch(eBg) { safeLog(null, 'e', "catch " + String(eBg)); }
var content = new android.graphics.drawable.GradientDrawable();
content.setShape(android.graphics.drawable.GradientDrawable.OVAL);
content.setColor(fillColor);
// # 描边:根据球体颜色亮度自动选择白/黑描边,确保任何背景下都可见
if (_usedKind !== "file" && _usedKind !== "app") {
try {
var Color = android.graphics.Color;
var lum = (Color.red(fillColor)*0.299 + Color.green(fillColor)*0.587 + Color.blue(fillColor)*0.114) / 255.0;
var strokeInt = lum > 0.55
? Color.parseColor("#33000000") // 浅球用半透明黑边
: Color.parseColor("#55FFFFFF"); // 深球用半透明白边
content.setStroke(this.dp(1), strokeInt);
} catch(eS) { safeLog(null, 'e', "catch " + String(eS)); }
}
var mask = new android.graphics.drawable.GradientDrawable();
mask.setShape(android.graphics.drawable.GradientDrawable.OVAL);
mask.setColor(android.graphics.Color.WHITE);
contentView.setBackground(new android.graphics.drawable.RippleDrawable(
android.content.res.ColorStateList.valueOf(rippleColor),
content,
mask
));
} catch(e) { safeLog(null, 'e', "catch " + String(e)); }
};
FloatBallAppWM.prototype.safeParseColor = function(hex, fallbackInt) {
// 这段代码的主要内容/用途:安全解析 #RRGGBB/#AARRGGBB解析失败直接回退避免 system_server 抛异常。
try { return android.graphics.Color.parseColor(String(hex)); } catch (e) { return fallbackInt; }
};
FloatBallAppWM.prototype.getPanelBgColorInt = function() {
// 这段代码的主要内容/用途:配合"白天/夜晚"两档主题,返回统一的背景颜色(不再依赖自动亮度推断)。
var isDark = this.isDarkTheme();
var dayBgHex = (this.config.THEME_DAY_BG_HEX != null) ? String(this.config.THEME_DAY_BG_HEX) : null;
var nightBgHex = (this.config.THEME_NIGHT_BG_HEX != null) ? String(this.config.THEME_NIGHT_BG_HEX) : null;
// # 兼容旧版默认配色:若仍为旧默认值,自动回退到莫奈色
if (dayBgHex === "#FAF4E3") dayBgHex = null;
if (nightBgHex === "#191928") nightBgHex = null;
// # 未配置时使用莫奈 surface 色作为回退
var dayFallback = this.ui.colors.bgLight || android.graphics.Color.parseColor("#F8F9FA");
var nightFallback = this.ui.colors.bgDark || android.graphics.Color.parseColor("#131314");
var base = isDark
? (nightBgHex ? this.safeParseColor(nightBgHex, nightFallback) : nightFallback)
: (dayBgHex ? this.safeParseColor(dayBgHex, dayFallback) : dayFallback);
// # 继承原配置面板背景透明度0~1
var a = 1.0;
try { a = Number(this.config.PANEL_BG_ALPHA); } catch (e1) { a = 0.85; }
if (!(a >= 0.0 && a <= 1.0)) a = 0.85;
var out = this.withAlpha(base, a);
try { _th_log(this.L, "d", "[t]bg isDark=" + isDark + " o=" + _th_hex(out)); } catch(e2) { safeLog(null, 'e', "catch " + String(e2)); }
return out;
};
FloatBallAppWM.prototype.getPanelTextColorInt = function(bgInt) {
// 这段代码的主要内容/用途:配合"白天/夜晚"两档主题,返回统一的文字颜色(不再依赖自动亮度推断)。
var isDark = this.isDarkTheme();
var dayTextHex = (this.config.THEME_DAY_TEXT_HEX != null) ? String(this.config.THEME_DAY_TEXT_HEX) : null;
var nightTextHex = (this.config.THEME_NIGHT_TEXT_HEX != null) ? String(this.config.THEME_NIGHT_TEXT_HEX) : null;
// # 兼容旧版默认配色:若仍为旧默认值,自动回退到莫奈色
if (dayTextHex === "#333333") dayTextHex = null;
if (nightTextHex === "#E6E6F0") nightTextHex = null;
// # 未配置时使用莫奈 onSurface 色作为回退
var dayFallback = this.ui.colors.textPriLight || android.graphics.Color.parseColor("#1F1F1F");
var nightFallback = this.ui.colors.textPriDark || android.graphics.Color.parseColor("#E3E3E3");
if (!isDark) return dayTextHex ? this.safeParseColor(dayTextHex, dayFallback) : dayFallback;
return nightTextHex ? this.safeParseColor(nightTextHex, nightFallback) : nightFallback;
};
FloatBallAppWM.prototype.applyTextColorRecursive = function(v, colorInt) {
// 这段代码的主要内容/用途:递归设置面板内所有 TextView 的文字颜色(标题/按钮文字/描述/设置项等)。
try {
if (!v) return;
if (v instanceof android.widget.TextView) {
v.setTextColor(colorInt);
}
if (v instanceof android.view.ViewGroup) {
var i, n = v.getChildCount();
for (i = 0; i < n; i++) {
this.applyTextColorRecursive(v.getChildAt(i), colorInt);
}
}
} catch(e0) { safeLog(null, 'e', "catch " + String(e0)); }
};
FloatBallAppWM.prototype.updatePanelBackground = function(panelView) {
// 这段代码的主要内容/用途:统一为"主面板/设置面板/查看器面板"应用背景与文字颜色(自动/亮/暗三档),并输出调试日志(命中哪个颜色)。
try {
var bg = new android.graphics.drawable.GradientDrawable();
bg.setCornerRadius(this.dp(22));
var bgInt = this.getPanelBgColorInt();
bg.setColor(bgInt);
// 轻量描边:亮色时更明显,暗色时也保留一点边界(不提供自定义输入,避免设置页复杂化)
var sw = this.dp(1);
var isDark = this.isDarkTheme();
var outlineColor = this.ui.colors._monetOutline || (isDark ? android.graphics.Color.parseColor("#8E918F") : android.graphics.Color.parseColor("#747775"));
var stroke = this.withAlpha(outlineColor, isDark ? 0.26 : 0.20);
try { bg.setStroke(sw, stroke); } catch(eS) { safeLog(null, 'e', "catch " + String(eS)); }
panelView.setBackground(bg);
var tc = this.getPanelTextColorInt(bgInt);
try { themeBgInt = bgInt; themeTextInt = tc; } catch(eT) { safeLog(null, 'e', "catch " + String(eT)); }
this.applyTextColorRecursive(panelView, tc);
try { _th_log(this.L, "d", "[t]apply bg=" + _th_hex(bgInt) + " tx=" + _th_hex(tc)); } catch(e) { safeLog(null, 'e', "catch " + String(e)); }
try {
_th_log(this.L, "d",
"[theme:apply] isDark=" + isDark +
" bg=" + _th_hex(bgInt) + " " + _th_argb(bgInt) +
" text=" + _th_hex(tc) + " " + _th_argb(tc) +
" stroke=" + _th_hex(stroke)
);
} catch(eL0) { safeLog(null, 'e', "catch " + String(eL0)); }
} catch (e) {
try { _th_log(this.L, "e", "[theme:apply] err=" + String(e)); } catch(eL1) { safeLog(null, 'e', "catch " + String(eL1)); }
}
};