diff --git a/README.md b/README.md index b9b6d93..dcfbc54 100644 --- a/README.md +++ b/README.md @@ -206,3 +206,5 @@ shortx.getShortXDir() + "/ToolHub/logs/init.log" - 修复图标选择器关闭后无法再次打开的问题 - 入口返回信息增加中文 `msg` / `syncMsg` 及 `updatedModules` 字段 - 优化入口返回信息格式 +- **设置面板改造**:取消“执行与查看器”、“悬浮球文字/大小/颜色”设置项 +- **悬浮球图标配置** 支持 ShortX 图标选择器(弹窗列表+搜索)和调色板拖拽选色 diff --git a/code/th_01_base.js b/code/th_01_base.js index 8c77058..1cde8ad 100644 --- a/code/th_01_base.js +++ b/code/th_01_base.js @@ -86,6 +86,7 @@ var ConfigValidator = { BALL_ICON_RES_ID: { type: "int", min: 0, max: 999999, default: 0 }, BALL_ICON_SIZE_DP: { type: "int", min: 16, max: 64, default: 22 }, BALL_PNG_MODE: { type: "int", min: 0, max: 2, default: 1 }, + BALL_IDLE_ALPHA: { type: "float", min: 0.1, max: 1.0, default: 0.6 }, // 交互配置 LONG_PRESS_MS: { type: "int", min: 200, max: 2000, default: 600 }, @@ -114,7 +115,6 @@ var ConfigValidator = { // 内容查看器配置 CONTENT_MAX_ROWS: { type: "int", min: 5, max: 100, default: 20 }, - CONTENT_VIEWER_TEXT_SP: { type: "int", min: 8, max: 24, default: 12 }, // ========== 以下配置在 Schema 中但原 ConfigValidator 中缺失 ========== // 图标文件配置 @@ -124,12 +124,6 @@ var ConfigValidator = { BALL_ICON_RES_NAME: { type: "string", default: "" }, BALL_ICON_TINT_HEX: { type: "string", default: "" }, - // 悬浮球外观 - BALL_IDLE_ALPHA: { type: "float", min: 0.1, max: 1.0, default: 0.6 }, - BALL_TEXT: { type: "string", default: "" }, - BALL_TEXT_COLOR_HEX: { type: "string", default: "" }, - BALL_TEXT_SIZE_SP: { type: "int", min: 6, max: 20, default: 10 }, - // 回弹动画配置 BOUNCE_DECAY: { type: "float", min: 0.3, max: 0.95, default: 0.72 }, BOUNCE_MAX_SCALE: { type: "float", min: 0.6, max: 0.99, default: 0.88 }, @@ -741,9 +735,6 @@ var ConfigManager = { BALL_ICON_RES_NAME: "", BALL_ICON_SIZE_DP: 22, BALL_ICON_TINT_HEX: "", - BALL_TEXT: "", - BALL_TEXT_SIZE_SP: 10, - BALL_TEXT_COLOR_HEX: "", BALL_IDLE_ALPHA: 0.6, PANEL_POS_GRAVITY: "bottom", PANEL_CUSTOM_OFFSET_Y: 0, @@ -766,8 +757,7 @@ var ConfigManager = { BALL_PANEL_GAP_DP: 10, LOG_ENABLE: true, LOG_DEBUG: true, - LOG_KEEP_DAYS: 3, -CONTENT_VIEWER_TEXT_SP: 12 + LOG_KEEP_DAYS: 3 }, defaultButtons: [ // # 默认按钮已迁移至 buttons.json @@ -785,20 +775,16 @@ CONTENT_VIEWER_TEXT_SP: 12 { type: "section", name: "悬浮球" }, { key: "BALL_SIZE_DP", name: "悬浮球大小(dp)", type: "int", min: 28, max: 120, step: 1 }, { key: "BALL_PANEL_GAP_DP", name: "球与面板间距(dp)", type: "int", min: 0, max: 60, step: 1 }, - { key: "BALL_ICON_TYPE", name: "图标类型(app/file/android/shortx)", type: "single_choice", options: [ + { key: "BALL_ICON_TYPE", name: "图标类型", type: "single_choice", options: [ { label: "应用图标 (app)", value: "app" }, { label: "文件图标 (file)", value: "file" }, { label: "系统图标 (android)", value: "android" }, { label: "ShortX内置 (shortx)", value: "shortx" } ]}, - { key: "BALL_ICON_PKG", name: "图标包名(app模式)", type: "text" }, { key: "BALL_ICON_FILE_PATH", name: "图标路径(file模式)", type: "text" }, - { key: "BALL_ICON_RES_NAME", name: "ShortX图标名(file/shortx模式兜底)", type: "text" }, - { key: "BALL_ICON_TINT_HEX", name: "图标着色(#RRGGBB, 空不着色)", type: "text" }, + { key: "BALL_ICON_RES_NAME", name: "ShortX图标", type: "ball_shortx_icon" }, + { key: "BALL_ICON_TINT_HEX", name: "图标颜色", type: "ball_color" }, { key: "BALL_IDLE_ALPHA", name: "闲置不透明度(0.1~1.0)", type: "float", min: 0.1, max: 1.0, step: 0.05 }, - { key: "BALL_TEXT", name: "悬浮球文字", type: "text" }, - { key: "BALL_TEXT_SIZE_SP", name: "文字大小(sp)", type: "int", min: 6, max: 20, step: 1 }, - { key: "BALL_TEXT_COLOR_HEX", name: "文字颜色(#RRGGBB)", type: "text" }, { type: "section", name: "面板布局" }, { key: "PANEL_ROWS", name: "面板可视行数(超出滚动)", type: "int", min: 1, max: 12, step: 1 }, @@ -843,9 +829,6 @@ CONTENT_VIEWER_TEXT_SP: 12 { key: "LONG_PRESS_HAPTIC_ENABLE", name: "长按震动反馈", type: "bool" }, { key: "LONG_PRESS_VIBRATE_MS", name: "震动时长(ms)", type: "int", min: 1, max: 120, step: 1 }, - { type: "section", name: "执行与查看器" }, -{ key: "CONTENT_VIEWER_TEXT_SP", name: "查看器文字大小(sp)", type: "int", min: 9, max: 18, step: 1 }, - { type: "section", name: "日志" }, { key: "LOG_ENABLE", name: "写文件日志", type: "bool" }, { key: "LOG_DEBUG", name: "详细日志(DEBUG)", type: "bool" }, @@ -877,7 +860,7 @@ CONTENT_VIEWER_TEXT_SP: 12 var needReset = false; if (s) { var sStr = JSON.stringify(s); - if (sStr.indexOf("ENABLE_SNAP_TO_EDGE") < 0 || sStr.indexOf("ENABLE_ANIMATIONS") < 0 || sStr.indexOf("BALL_IDLE_ALPHA") < 0 || sStr.indexOf("PANEL_POS_GRAVITY") < 0 || sStr.indexOf("single_choice") < 0) { + if (sStr.indexOf("ENABLE_SNAP_TO_EDGE") < 0 || sStr.indexOf("ENABLE_ANIMATIONS") < 0 || sStr.indexOf("BALL_IDLE_ALPHA") < 0 || sStr.indexOf("PANEL_POS_GRAVITY") < 0 || sStr.indexOf("single_choice") < 0 || sStr.indexOf("ball_shortx_icon") < 0 || sStr.indexOf("ball_color") < 0) { needReset = true; } } else { diff --git a/code/th_05_persistence.js b/code/th_05_persistence.js index 64ac2c6..64db8d4 100644 --- a/code/th_05_persistence.js +++ b/code/th_05_persistence.js @@ -234,7 +234,7 @@ FloatBallAppWM.prototype.applyImmediateEffectsForKey = function(k) { } catch (eLK) {} return; } - if (k === "BALL_SIZE_DP" || k === "BALL_PNG_MODE" || k === "BALL_ICON_TYPE" || k === "BALL_ICON_PKG" || k === "BALL_ICON_FILE_PATH" || k === "BALL_ICON_RES_ID" || k === "BALL_ICON_RES_NAME" || k === "BALL_ICON_SIZE_DP" || k === "BALL_ICON_TINT_HEX" || k === "BALL_TEXT" || k === "BALL_TEXT_SIZE_SP" || k === "BALL_TEXT_COLOR_HEX" || k === "BALL_ICON_TEXT_GAP_DP") { this.rebuildBallForNewSize(); return; } + if (k === "BALL_SIZE_DP" || k === "BALL_PNG_MODE" || k === "BALL_ICON_TYPE" || k === "BALL_ICON_FILE_PATH" || k === "BALL_ICON_RES_ID" || k === "BALL_ICON_RES_NAME" || k === "BALL_ICON_SIZE_DP" || k === "BALL_ICON_TINT_HEX" || k === "BALL_ICON_TEXT_GAP_DP") { this.rebuildBallForNewSize(); return; } if (k === "PANEL_ROWS" || k === "PANEL_COLS" || k === "PANEL_ITEM_SIZE_DP" || k === "PANEL_GAP_DP" || diff --git a/code/th_13_panel_ui.js b/code/th_13_panel_ui.js index 829ff0d..4e330e2 100644 --- a/code/th_13_panel_ui.js +++ b/code/th_13_panel_ui.js @@ -363,6 +363,265 @@ FloatBallAppWM.prototype.createSettingItemView = function(item, parent, needDivi row.addView(rg); parent.addView(row); + } else if (item.type === "ball_shortx_icon") { + // === 悬浮球 ShortX 图标选择器 === + row.setOrientation(android.widget.LinearLayout.VERTICAL); + var tv = new android.widget.TextView(context); + tv.setText(String(item.name)); + tv.setTextColor(textColor); + tv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 16); + row.addView(tv); + + var iconRow = new android.widget.LinearLayout(context); + iconRow.setOrientation(android.widget.LinearLayout.HORIZONTAL); + iconRow.setGravity(android.view.Gravity.CENTER_VERTICAL); + iconRow.setPadding(0, self.dp(8), 0, 0); + + var previewIv = new android.widget.ImageView(context); + var previewIvLp = new android.widget.LinearLayout.LayoutParams(self.dp(36), self.dp(36)); + previewIvLp.rightMargin = self.dp(10); + previewIv.setLayoutParams(previewIvLp); + previewIv.setScaleType(android.widget.ImageView.ScaleType.FIT_CENTER); + try { + var curIconName = String(self.getPendingValue(item.key) || ""); + var curTint = String(self.getPendingValue("BALL_ICON_TINT_HEX") || ""); + if (curIconName) { + var dr = self.resolveShortXDrawable(curIconName, curTint); + if (dr) previewIv.setImageDrawable(dr); + } + } catch(ePreview) {} + iconRow.addView(previewIv); + + var nameTv = new android.widget.TextView(context); + nameTv.setTextColor(secColor); + nameTv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 13); + nameTv.setText(String(self.getPendingValue(item.key) || "未选择")); + var nameTvLp = new android.widget.LinearLayout.LayoutParams(0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT, 1); + nameTv.setLayoutParams(nameTvLp); + iconRow.addView(nameTv); + + var btnPick = self.ui.createFlatButton(self, "选择", primary, function() { + self.touchActivity(); + try { + var catalog = self.getShortXIconCatalog(); + if (!catalog || catalog.length === 0) { + self.toast("图标库未加载,请检查 ShortX 是否安装"); + return; + } + + var dialogView = new android.widget.LinearLayout(context); + dialogView.setOrientation(android.widget.LinearLayout.VERTICAL); + dialogView.setPadding(self.dp(16), self.dp(16), self.dp(16), self.dp(16)); + + var searchEt = new android.widget.EditText(context); + searchEt.setHint("搜索图标..."); + searchEt.setTextColor(textColor); + searchEt.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 14); + searchEt.setBackground(self.ui.createRoundDrawable(isDark ? C.inputBgDark : C.inputBgLight, self.dp(6))); + searchEt.setPadding(self.dp(8), self.dp(8), self.dp(8), self.dp(8)); + searchEt.setSingleLine(true); + dialogView.addView(searchEt); + + var listView = new android.widget.ListView(context); + var listLp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, self.dp(360)); + listLp.topMargin = self.dp(8); + listView.setLayoutParams(listLp); + dialogView.addView(listView); + + var adapterData = []; + var i; + for (i = 0; i < catalog.length; i++) { + adapterData.push(String(catalog[i].shortName || catalog[i].name)); + } + + var adapter = new android.widget.ArrayAdapter(context, android.R.layout.simple_list_item_1, adapterData); + listView.setAdapter(adapter); + + var dialog = new android.app.AlertDialog.Builder(context) + .setTitle("选择 ShortX 图标") + .setView(dialogView) + .setNegativeButton("取消", null) + .create(); + + listView.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener({ + onItemClick: function(parent, view, position, id) { + try { + var selectedName = String(adapter.getItem(position)); + self.setPendingValue(item.key, selectedName); + nameTv.setText(selectedName); + try { + var tint = String(self.getPendingValue("BALL_ICON_TINT_HEX") || ""); + var dr = self.resolveShortXDrawable(selectedName, tint); + if (dr) previewIv.setImageDrawable(dr); + else previewIv.setImageDrawable(null); + } catch(eDr) {} + dialog.dismiss(); + if (self.state.previewMode) self.rebuildBallForNewSize(true); + } catch(eClick) {} + } + })); + + searchEt.addTextChangedListener(new android.text.TextWatcher({ + beforeTextChanged: function(s, start, count, after) {}, + onTextChanged: function(s, start, before, count) {}, + afterTextChanged: function(s) { + try { + var q = String(s).toLowerCase(); + var filtered = []; + var j; + for (j = 0; j < catalog.length; j++) { + var n = String(catalog[j].shortName || catalog[j].name).toLowerCase(); + if (n.indexOf(q) >= 0) filtered.push(String(catalog[j].shortName || catalog[j].name)); + } + var newAdapter = new android.widget.ArrayAdapter(context, android.R.layout.simple_list_item_1, filtered); + listView.setAdapter(newAdapter); + } catch(eFilter) {} + } + })); + + dialog.show(); + } catch(eDialog) { + self.toast("打开图标选择器失败: " + String(eDialog)); + } + }); + iconRow.addView(btnPick); + row.addView(iconRow); + parent.addView(row); + + } else if (item.type === "ball_color") { + // === 悬浮球图标颜色选择器 === + row.setOrientation(android.widget.LinearLayout.VERTICAL); + var tv = new android.widget.TextView(context); + tv.setText(String(item.name)); + tv.setTextColor(textColor); + tv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 16); + row.addView(tv); + + var colorRow = new android.widget.LinearLayout(context); + colorRow.setOrientation(android.widget.LinearLayout.HORIZONTAL); + colorRow.setGravity(android.view.Gravity.CENTER_VERTICAL); + colorRow.setPadding(0, self.dp(8), 0, 0); + + var colorDot = new android.view.View(context); + var colorDotLp = new android.widget.LinearLayout.LayoutParams(self.dp(28), self.dp(28)); + colorDotLp.rightMargin = self.dp(10); + colorDot.setLayoutParams(colorDotLp); + try { + var curHex = String(self.getPendingValue(item.key) || ""); + if (curHex) { + colorDot.setBackground(self.ui.createRoundDrawable(android.graphics.Color.parseColor(curHex), self.dp(14))); + } else { + colorDot.setBackground(self.ui.createRoundDrawable(0xFFCCCCCC | 0, self.dp(14))); + } + } catch(eDot) { + colorDot.setBackground(self.ui.createRoundDrawable(0xFFCCCCCC | 0, self.dp(14))); + } + colorRow.addView(colorDot); + + var colorValueTv = new android.widget.TextView(context); + colorValueTv.setTextColor(secColor); + colorValueTv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 13); + colorValueTv.setText(String(self.getPendingValue(item.key) || "默认")); + var colorValueLp = new android.widget.LinearLayout.LayoutParams(0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT, 1); + colorValueTv.setLayoutParams(colorValueLp); + colorRow.addView(colorValueTv); + + var commonColors = [ + "#F44336", "#E91E63", "#9C27B0", "#673AB7", "#3F51B5", + "#2196F3", "#03A9F4", "#00BCD4", "#009688", "#4CAF50", + "#8BC34A", "#CDDC39", "#FFEB3B", "#FFC107", "#FF9800", + "#FF5722", "#795548", "#9E9E9E", "#607D8B", "#000000", "#FFFFFF" + ]; + + var btnColor = self.ui.createFlatButton(self, "调色板", primary, function() { + self.touchActivity(); + try { + var dialogView = new android.widget.LinearLayout(context); + dialogView.setOrientation(android.widget.LinearLayout.VERTICAL); + dialogView.setPadding(self.dp(16), self.dp(16), self.dp(16), self.dp(16)); + + var scroll = new android.widget.ScrollView(context); + var scrollBox = new android.widget.LinearLayout(context); + scrollBox.setOrientation(android.widget.LinearLayout.VERTICAL); + scroll.addView(scrollBox); + dialogView.addView(scroll); + + var grid = new android.widget.GridLayout(context); + try { grid.setColumnCount(5); } catch(e){} + var gridLp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); + grid.setLayoutParams(gridLp); + scrollBox.addView(grid); + + var ci; + for (ci = 0; ci < commonColors.length; ci++) { + (function(hex) { + var colorBtn = new android.widget.TextView(context); + colorBtn.setLayoutParams(new android.widget.GridLayout.LayoutParams(self.dp(44), self.dp(44))); + try { + colorBtn.setBackground(self.ui.createRoundDrawable(android.graphics.Color.parseColor(hex), self.dp(8))); + } catch(eBg) {} + colorBtn.setOnClickListener(new android.view.View.OnClickListener({ + onClick: function(v) { + try { + self.setPendingValue(item.key, hex); + colorValueTv.setText(hex); + try { colorDot.setBackground(self.ui.createRoundDrawable(android.graphics.Color.parseColor(hex), self.dp(14))); } catch(eDot2) {} + if (self.state.previewMode) self.rebuildBallForNewSize(true); + } catch(eSet) {} + } + })); + grid.addView(colorBtn); + })(commonColors[ci]); + } + + var inputEt = new android.widget.EditText(context); + inputEt.setHint("手动输入 #RRGGBB"); + inputEt.setTextColor(textColor); + inputEt.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 14); + inputEt.setBackground(self.ui.createRoundDrawable(isDark ? C.inputBgDark : C.inputBgLight, self.dp(6))); + inputEt.setPadding(self.dp(8), self.dp(8), self.dp(8), self.dp(8)); + inputEt.setSingleLine(true); + inputEt.setText(String(self.getPendingValue(item.key) || "")); + var inputLp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); + inputLp.topMargin = self.dp(12); + inputEt.setLayoutParams(inputLp); + scrollBox.addView(inputEt); + + var dialog = new android.app.AlertDialog.Builder(context) + .setTitle("选择图标颜色") + .setView(dialogView) + .setPositiveButton("确定", new android.content.DialogInterface.OnClickListener({ + onClick: function(dlg, which) { + try { + var val = String(inputEt.getText() || "").replace(/^\s+|\s+$/g, ""); + if (val) { + if (val.indexOf("#") !== 0) val = "#" + val; + android.graphics.Color.parseColor(val); + self.setPendingValue(item.key, val); + colorValueTv.setText(val); + try { colorDot.setBackground(self.ui.createRoundDrawable(android.graphics.Color.parseColor(val), self.dp(14))); } catch(eDot3) {} + } else { + self.setPendingValue(item.key, ""); + colorValueTv.setText("默认"); + try { colorDot.setBackground(self.ui.createRoundDrawable(0xFFCCCCCC | 0, self.dp(14))); } catch(eDot4) {} + } + if (self.state.previewMode) self.rebuildBallForNewSize(true); + } catch(eOk) { + self.toast("颜色格式无效"); + } + } + })) + .setNegativeButton("取消", null) + .create(); + dialog.show(); + } catch(eDialog) { + self.toast("打开调色板失败: " + String(eDialog)); + } + }); + colorRow.addView(btnColor); + row.addView(colorRow); + parent.addView(row); + } else { // 兜底文本 var tv = new android.widget.TextView(context);