feat: app-style button manager page

This commit is contained in:
7015725
2026-05-12 19:11:29 +08:00
parent c41e1479b9
commit c1643924b7
4 changed files with 120 additions and 8 deletions

View File

@@ -77,6 +77,9 @@ function FloatBallAppWM(logger) {
pendingUserCfg: null, pendingUserCfg: null,
pendingDirty: false, pendingDirty: false,
// 按钮管理首页:搜索过滤状态
buttonManagerQuery: "",
closing: false closing: false
}; };

View File

@@ -494,6 +494,72 @@ function __scDrawableToBitmap(drawable, targetPx) {
} }
} }
FloatBallAppWM.prototype.matchesButtonManagerQuery = function(btnCfg, query) {
try {
var q = String(query || "").replace(/^\s+|\s+$/g, "").toLowerCase();
if (!q) return true;
var b = btnCfg || {};
var hay = String(b.title || "") + " " + String(b.type || "") + " " + String(b.cmd || "") + " " + String(b.pkg || "") + " " + String(b.action || "") + " " + String(b.intent || "") + " " + String(b.shortcutName || "") + " " + String(b.iconResName || "");
return hay.toLowerCase().indexOf(q) >= 0;
} catch(e) { return true; }
};
FloatBallAppWM.prototype.createButtonManagerSummaryCard = function(parent, totalCount, enabledCount, disabledCount) {
var isDark = this.isDarkTheme();
var C = this.ui.colors;
var card = new android.widget.LinearLayout(context);
card.setOrientation(android.widget.LinearLayout.VERTICAL);
card.setPadding(this.dp(14), this.dp(10), this.dp(14), this.dp(10));
card.setBackground(this.ui.createRoundDrawable(isDark ? this.withAlpha(C.cardDark, 0.88) : this.withAlpha(C.cardLight, 0.92), this.dp(14)));
try { card.setElevation(this.dp(2)); } catch(eElev) { safeLog(null, 'e', "catch " + String(eElev)); }
var title = new android.widget.TextView(context);
title.setText("按钮管理首页");
title.setTextColor(isDark ? C.textPriDark : C.textPriLight);
title.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 15);
title.setTypeface(null, android.graphics.Typeface.BOLD);
card.addView(title);
var sub = new android.widget.TextView(context);
sub.setText("共 " + totalCount + " 个 · 已启用 " + enabledCount + " · 已禁用 " + disabledCount);
sub.setTextColor(isDark ? C.textSecDark : C.textSecLight);
sub.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12);
sub.setPadding(0, this.dp(4), 0, 0);
card.addView(sub);
var lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(this.dp(2), this.dp(2), this.dp(2), this.dp(8));
parent.addView(card, lp);
};
FloatBallAppWM.prototype.createButtonEditorSectionCard = function(parent, title, desc) {
var isDark = this.isDarkTheme();
var C = this.ui.colors;
var box = new android.widget.LinearLayout(context);
box.setOrientation(android.widget.LinearLayout.VERTICAL);
box.setPadding(this.dp(12), this.dp(10), this.dp(12), this.dp(10));
box.setBackground(this.ui.createRoundDrawable(isDark ? this.withAlpha(C.cardDark, 0.72) : this.withAlpha(C.cardLight, 0.78), this.dp(14)));
var tv = new android.widget.TextView(context);
tv.setText(String(title || ""));
tv.setTextColor(isDark ? C.textPriDark : C.textPriLight);
tv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 14);
tv.setTypeface(null, android.graphics.Typeface.BOLD);
box.addView(tv);
if (desc) {
var dv = new android.widget.TextView(context);
dv.setText(String(desc));
dv.setTextColor(isDark ? C.textSecDark : C.textSecLight);
dv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 11);
dv.setPadding(0, this.dp(3), 0, 0);
box.addView(dv);
}
var lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(0, this.dp(8), 0, this.dp(8));
parent.addView(box, lp);
return box;
};
FloatBallAppWM.prototype.buildButtonEditorPanelView = function() { FloatBallAppWM.prototype.buildButtonEditorPanelView = function() {
var self = this; var self = this;
// # 状态管理editingIndex (null=列表, -1=新增, >=0=编辑) // # 状态管理editingIndex (null=列表, -1=新增, >=0=编辑)
@@ -578,6 +644,41 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() {
// 暴露 Header 给 DragListener // 暴露 Header 给 DragListener
panel.setTag(header); panel.setTag(header);
var enabledCount = 0;
var disabledCount = 0;
try {
for (var ci = 0; ci < buttons.length; ci++) {
if (buttons[ci] && buttons[ci].enabled === false) disabledCount++;
else enabledCount++;
}
} catch(eCnt) { safeLog(null, 'e', "catch " + String(eCnt)); }
self.createButtonManagerSummaryCard(panel, buttons.length, enabledCount, disabledCount);
var searchRow = new android.widget.LinearLayout(context);
searchRow.setOrientation(android.widget.LinearLayout.HORIZONTAL);
searchRow.setGravity(android.view.Gravity.CENTER_VERTICAL);
searchRow.setPadding(0, 0, 0, self.dp(8));
var searchInput = self.ui.createInputGroup(self, "搜索按钮", self.state.buttonManagerQuery || "", false, "标题 / 类型 / 包名 / 命令");
var searchInputLp = new android.widget.LinearLayout.LayoutParams(0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT);
searchInputLp.weight = 1;
searchRow.addView(searchInput.view, searchInputLp);
var btnSearch = self.ui.createFlatButton(self, "搜索", C.primary, function() {
try { self.state.buttonManagerQuery = String(searchInput.getValue ? searchInput.getValue() : ""); } catch(eQ) { self.state.buttonManagerQuery = ""; }
self.state.btnEditorListScrollY = 0;
refreshPanel();
});
searchRow.addView(btnSearch);
var btnClearSearch = self.ui.createFlatButton(self, "清空", subTextColor, function() {
self.state.buttonManagerQuery = "";
self.state.btnEditorListScrollY = 0;
refreshPanel();
});
searchRow.addView(btnClearSearch);
panel.addView(searchRow);
var activeQuery = String(self.state.buttonManagerQuery || "");
var visibleCount = 0;
// 列表区域 // 列表区域
var scroll = new android.widget.ScrollView(context); var scroll = new android.widget.ScrollView(context);
__btnEditorListScroll = scroll; // # 列表滚动位置保持:让 refreshPanel 能拿到当前列表的滚动位置 __btnEditorListScroll = scroll; // # 列表滚动位置保持:让 refreshPanel 能拿到当前列表的滚动位置
@@ -589,6 +690,8 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() {
for (var i = 0; i < buttons.length; i++) { for (var i = 0; i < buttons.length; i++) {
(function(idx) { (function(idx) {
var btnCfg = buttons[idx]; var btnCfg = buttons[idx];
if (!self.matchesButtonManagerQuery(btnCfg, activeQuery)) return;
visibleCount++;
// # 启用/禁用状态:禁用项在按钮页不显示,但在管理页需要标识出来 // # 启用/禁用状态:禁用项在按钮页不显示,但在管理页需要标识出来
var __enabled = true; var __enabled = true;
@@ -805,7 +908,7 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() {
} }
// 空状态提示 // 空状态提示
if (buttons.length === 0) { if (visibleCount === 0) {
var emptyBox = new android.widget.LinearLayout(context); var emptyBox = new android.widget.LinearLayout(context);
emptyBox.setOrientation(android.widget.LinearLayout.VERTICAL); emptyBox.setOrientation(android.widget.LinearLayout.VERTICAL);
emptyBox.setGravity(android.view.Gravity.CENTER); emptyBox.setGravity(android.view.Gravity.CENTER);
@@ -819,7 +922,7 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() {
emptyBox.addView(emptyIcon); emptyBox.addView(emptyIcon);
var emptyTv = new android.widget.TextView(context); var emptyTv = new android.widget.TextView(context);
emptyTv.setText("暂无按钮,点击右上角新增"); emptyTv.setText(activeQuery ? "没有匹配的按钮,点清空查看全部" : "暂无按钮,点击右上角新增");
emptyTv.setTextColor(subTextColor); emptyTv.setTextColor(subTextColor);
emptyTv.setPadding(0, self.dp(16), 0, 0); emptyTv.setPadding(0, self.dp(16), 0, 0);
emptyBox.addView(emptyTv); emptyBox.addView(emptyTv);
@@ -911,6 +1014,8 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() {
editHint.setPadding(self.dp(4), 0, 0, self.dp(16)); editHint.setPadding(self.dp(4), 0, 0, self.dp(16));
form.addView(editHint); form.addView(editHint);
self.createButtonEditorSectionCard(form, "基础信息", "按钮名称与基础标识");
// 1. 标题 (Title) // 1. 标题 (Title)
var topArea = new android.widget.LinearLayout(context); var topArea = new android.widget.LinearLayout(context);
topArea.setOrientation(android.widget.LinearLayout.HORIZONTAL); topArea.setOrientation(android.widget.LinearLayout.HORIZONTAL);
@@ -930,6 +1035,8 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() {
form.addView(topArea); form.addView(topArea);
self.createButtonEditorSectionCard(form, "图标外观", "选择图标来源、ShortX 图标和颜色");
// 1.5 图标选择(文件路径 或 ShortX 内置图标 二选一) // 1.5 图标选择(文件路径 或 ShortX 内置图标 二选一)
var iconSelectWrap = new android.widget.LinearLayout(context); var iconSelectWrap = new android.widget.LinearLayout(context);
iconSelectWrap.setOrientation(android.widget.LinearLayout.VERTICAL); iconSelectWrap.setOrientation(android.widget.LinearLayout.VERTICAL);
@@ -2097,6 +2204,8 @@ FloatBallAppWM.prototype.buildButtonEditorPanelView = function() {
})); }));
self.createButtonEditorSectionCard(form, "动作设置", "选择点击后执行的动作类型与参数");
// 2. 动作类型(自动换行:用 GridLayout 稳定实现) // 2. 动作类型(自动换行:用 GridLayout 稳定实现)
// 这段代码的主要内容/用途把「Shell/App/广播/Intent/快捷方式」做成会自动换行的单选框区域。 // 这段代码的主要内容/用途把「Shell/App/广播/Intent/快捷方式」做成会自动换行的单选框区域。
// 说明:之前用"多行 LinearLayout + 手工测量"在部分 ROM/布局时序下会出现宽度为 0 或不渲染,导致"单选框看不见"。 // 说明:之前用"多行 LinearLayout + 手工测量"在部分 ROM/布局时序下会出现宽度为 0 或不渲染,导致"单选框看不见"。

View File

@@ -6,8 +6,8 @@
"size": 52546 "size": 52546
}, },
"th_02_core.js": { "th_02_core.js": {
"sha256": "3ccdff641f8cd1e185812f24df0baf65c7749a320d29549340f884575ac4fa9d", "sha256": "24363adc7156feda5c2f1fcbb84c96a4378ffec4b40251bd27d0123329acf054",
"size": 4127 "size": 4203
}, },
"th_03_icon.js": { "th_03_icon.js": {
"sha256": "717f7f37474d3616c2cd944581455f600020a850ec8812100d0546ec1302c987", "sha256": "717f7f37474d3616c2cd944581455f600020a850ec8812100d0546ec1302c987",
@@ -54,8 +54,8 @@
"size": 20386 "size": 20386
}, },
"th_14_panels.js": { "th_14_panels.js": {
"sha256": "c65a2ac74f69f3763f5effd8533a27d1e1bd47660c9f2beb8961ad60d012058a", "sha256": "5ed0c2ece79efe2d67e3c0b6f85e8e2b24980d4a46de3fafc7a461940801a936",
"size": 226409 "size": 231980
}, },
"th_15_extra.js": { "th_15_extra.js": {
"sha256": "b48c16ea5cbe5b9033d593eb18ef3028c0d661c65cbf2ad8f3f650ee55bbe2e4", "sha256": "b48c16ea5cbe5b9033d593eb18ef3028c0d661c65cbf2ad8f3f650ee55bbe2e4",
@@ -68,5 +68,5 @@
}, },
"keyId": "toolhub-targets-2026-rsa3072", "keyId": "toolhub-targets-2026-rsa3072",
"schema": 2, "schema": 2,
"version": 20260512093034 "version": 20260512111055
} }

View File

@@ -1 +1 @@
QeDCiPelzRAPjhtoPyf6861/eQKyfrIqV66PERJbtyu0n8oJsw34JQxQQE5DjMURszMTjV/scS8cxytrSJtb81W1vqE3LGbRwDdVHt6Wdf+n62HMkRGdg7MzBQma5AVVMPsw3ElvfVf5q46nRsgrCmRXJ4j5d3a8SqlTm+HxtsmaDOjGn+mqZynnHFgGwbj0YQtQXYE+9GUt/tk34ffksG30k0+fRkQ71zsCad9XAHrUMHDJvHOCIZNg+rpqYlZDXxZsTyiXSCmb+IcW5G8jZMDo6K5D650a8TYdh1MfcBl7S0qhsuPPNwqxm9SbtWHwoKg4EDlpKNrRRXupm/4kIGRD+VVMYnsmbIUyGwr561JXqD1IgjyuJY52SQIG+ZEaLy77c/1JQ8tuxzufTcKfIyhr5Q/hbnZ6sRZ9eD791q68ZfQ7HZ9Gzv6kD+3rnnvLNC0kwWnpeeZipa9+qu1xbWkBSWj++Fu9ZBD9CHrB30zF7Tqg2gk3XR8bPttonU0M G+Vg48mDhYHupFhgZdH7mx2kn6AJiHNOVBfkuULMURTDG4yNeIomcjldi/mw9zTdDrgzmcHnvJEe/YnisYF21bwJ0OE7fWVTHqjWQFQq3SHMIWVjWaP2Jz7r+tinvAQfpGgOj3lDPddDthIMd59hCs1vcM2LWT/RegEGybVquv9LBnHybIuawYuuJeFP5jptKghw016FZ7veLpIlvij7RwptgUyknT6VaKpcsRIInTtlvUPvjrMNqfN2rH1X+OhzaxFEhUnxBU5P2m5TLZsb70hCy456GaBN8i0yOanIMWCfNy9gkUzYwW87Vf5qEvKU4Y+/Vp9mKcevN6O7DrKsOld6TXqFYyf0k4cw5SOq/mSZEmMaJejrwXkXeK/VNwn1zKqGWx/Vax3mWfYl5zdO9QoPMjWx0WRVqBwMaUV6lnWx4WOwhONyOhbwYhg85DgnzO+dtxyJsDwh3F9w3sjNoqwQTVWTAklvh6lhlOB9dR6ZL8sqLFoh0DOewEK7qYIC