diff --git a/code/th_14_panels.js b/code/th_14_panels.js index 266c02a..efde93f 100644 --- a/code/th_14_panels.js +++ b/code/th_14_panels.js @@ -35,6 +35,54 @@ FloatBallAppWM.prototype.isSettingsMonetTheme = function(cfgTpl) { try { return String((cfgTpl || this.config).SETTINGS_THEME || "animal") === "monet"; } catch(e) { return false; } }; + +FloatBallAppWM.prototype.getSettingsResponsiveSpec = function() { + try { + if (this.getToolAppResponsiveSpec) return this.getToolAppResponsiveSpec(); + } catch(e) {} + var sw = Math.max(1, Number(this.state.screen && this.state.screen.w || 0)); + var sh = Math.max(1, Number(this.state.screen && this.state.screen.h || 0)); + return { + screenW: sw, screenH: sh, isLandscape: sw > sh, + isCompactWidth: sw < this.dp(600), + isMediumWidth: sw >= this.dp(600) && sw < this.dp(840), + isExpandedWidth: sw >= this.dp(840) && sw < this.dp(1200), + isWideWidth: sw >= this.dp(1200), + gridColumnCount: sw >= this.dp(840) ? 2 : 1, + useSideBySide: sw > sh && sw >= this.dp(840), + leftPaneWidth: sw >= this.dp(1200) ? this.dp(340) : this.dp(300), + cardRadius: sw >= this.dp(840) ? this.dp(20) : this.dp(24), + itemRadius: sw >= this.dp(840) ? this.dp(16) : this.dp(18), + cardGap: sw >= this.dp(840) ? this.dp(14) : this.dp(8) + }; +}; + +FloatBallAppWM.prototype.createSettingsGridContainer = function(columns) { + var grid = new android.widget.GridLayout(context); + grid.setColumnCount(Math.max(1, Number(columns || 1))); + grid.setUseDefaultMargins(false); + grid.setAlignmentMode(android.widget.GridLayout.ALIGN_BOUNDS); + return grid; +}; + +FloatBallAppWM.prototype.addSettingsGridChild = function(parent, child, columns) { + try { + var isGrid = parent && String(parent.getClass && parent.getClass().getName && parent.getClass().getName()).indexOf("GridLayout") >= 0; + if (isGrid) { + var glp = new android.widget.GridLayout.LayoutParams(); + glp.width = 0; + glp.height = android.widget.GridLayout.LayoutParams.WRAP_CONTENT; + glp.columnSpec = android.widget.GridLayout.spec(android.widget.GridLayout.UNDEFINED, 1, 1); + glp.setMargins(this.dp(6), this.dp(6), this.dp(6), this.dp(6)); + parent.addView(child, glp); + return; + } + } catch(eGrid) {} + var lp = new android.widget.LinearLayout.LayoutParams(-1, -2); + lp.setMargins(this.dp(2), this.dp(4), this.dp(2), this.dp(4)); + parent.addView(child, lp); +}; + FloatBallAppWM.prototype.getSettingsGroupDefs = function() { var cfgTpl = null; try { cfgTpl = this.state.pendingUserCfg ? this.state.pendingUserCfg : this.config; } catch(eCfg) { cfgTpl = this.config; } @@ -172,9 +220,12 @@ FloatBallAppWM.prototype.createSettingsHomeEntry = function(parent, title, desc, var row = new android.widget.LinearLayout(context); row.setOrientation(android.widget.LinearLayout.HORIZONTAL); row.setGravity(android.view.Gravity.CENTER_VERTICAL); - row.setPadding(this.dp(14), this.dp(12), this.dp(12), this.dp(12)); - row.setBackground(this.ui.createRippleDrawable(T.card, this.withAlpha(T.primary, isDark ? 0.18 : 0.12), this.dp(18))); - try { row.setElevation(this.dp(useMonet ? 1 : 3)); } catch(eElev) { safeLog(null, 'e', "catch " + String(eElev)); } + var spec = this.getSettingsResponsiveSpec ? this.getSettingsResponsiveSpec() : null; + var itemRadius = spec ? spec.itemRadius : this.dp(18); + row.setPadding(this.dp(14), spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(10) : this.dp(12), this.dp(12), spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(10) : this.dp(12)); + row.setMinimumHeight(spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(76) : this.dp(82)); + row.setBackground(this.ui.createRippleDrawable(T.card, this.withAlpha(T.primary, isDark ? 0.18 : 0.12), itemRadius)); + try { row.setElevation(this.dp(useMonet ? 1 : ((spec && (spec.isExpandedWidth || spec.isWideWidth)) ? 1 : 3))); } catch(eElev) { safeLog(null, 'e', "catch " + String(eElev)); } var badge = new android.widget.TextView(context); badge.setText(this.getSettingsHomeIcon ? this.getSettingsHomeIcon(title) : "✦"); badge.setTextColor(T.primaryDeep); @@ -182,7 +233,8 @@ FloatBallAppWM.prototype.createSettingsHomeEntry = function(parent, title, desc, badge.setGravity(android.view.Gravity.CENTER); badge.setTypeface(null, android.graphics.Typeface.BOLD); badge.setBackground(this.ui.createStrokeDrawable(T.primarySoft, this.withAlpha(T.primaryDeep, isDark ? 0.30 : 0.22), this.dp(1), this.dp(14))); - var badgeLp = new android.widget.LinearLayout.LayoutParams(this.dp(44), this.dp(44)); + var iconSize = spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(42) : this.dp(44); + var badgeLp = new android.widget.LinearLayout.LayoutParams(iconSize, iconSize); badgeLp.setMargins(0, 0, this.dp(12), 0); row.addView(badge, badgeLp); var texts = new android.widget.LinearLayout(context); @@ -212,9 +264,12 @@ FloatBallAppWM.prototype.createSettingsHomeEntry = function(parent, title, desc, try { self.touchActivity(); } catch(eT) {} try { if (onClick) onClick(); } catch(eC) { try { self.toast("打开失败: " + String(eC)); } catch(eToast) {} } }})); - var lp = new android.widget.LinearLayout.LayoutParams(-1, -2); - lp.setMargins(this.dp(2), this.dp(4), this.dp(2), this.dp(4)); - parent.addView(row, lp); + if (this.addSettingsGridChild) this.addSettingsGridChild(parent, row, spec ? spec.gridColumnCount : 1); + else { + var lp = new android.widget.LinearLayout.LayoutParams(-1, -2); + lp.setMargins(this.dp(2), this.dp(4), this.dp(2), this.dp(4)); + parent.addView(row, lp); + } }; FloatBallAppWM.prototype.createIslandWelcomeCard = function(parent, statusLabel, statusValue, statusBg, statusStroke, statusValueColor) { @@ -223,19 +278,21 @@ FloatBallAppWM.prototype.createIslandWelcomeCard = function(parent, statusLabel, var T = this.getAnimalIslandTheme(); var cfgTpl = this.state.pendingUserCfg ? this.state.pendingUserCfg : this.config; this.applySettingsTheme(T, isDark, C, cfgTpl); + var spec = this.getSettingsResponsiveSpec ? this.getSettingsResponsiveSpec() : null; + var compactWelcome = spec && (spec.isLandscape || spec.isExpandedWidth || spec.isWideWidth); var card = new android.widget.LinearLayout(context); card.setOrientation(android.widget.LinearLayout.HORIZONTAL); card.setGravity(android.view.Gravity.CENTER_VERTICAL); - card.setPadding(this.dp(14), this.dp(14), this.dp(14), this.dp(14)); - card.setBackground(this.ui.createStrokeDrawable(T.card, this.withAlpha(T.primaryDeep, isDark ? 0.28 : 0.22), this.dp(1), this.dp(24))); - try { card.setElevation(this.dp(5)); } catch(eElev) {} + card.setPadding(this.dp(14), compactWelcome ? this.dp(10) : this.dp(14), this.dp(14), compactWelcome ? this.dp(10) : this.dp(14)); + card.setBackground(this.ui.createStrokeDrawable(T.card, this.withAlpha(T.primaryDeep, isDark ? 0.22 : 0.18), this.dp(1), spec ? spec.cardRadius : this.dp(24))); + try { card.setElevation(this.dp(compactWelcome ? 2 : 4)); } catch(eElev) {} var island = new android.widget.TextView(context); island.setText("☁ ︵\n🌴🏠⛱\n≈≈≈≈"); island.setGravity(android.view.Gravity.CENTER); - island.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 20); + island.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, compactWelcome ? 16 : 20); island.setTextColor(T.primaryDeep); island.setBackground(this.ui.createRoundDrawable(this.withAlpha(T.primarySoft, isDark ? 0.78 : 0.96), this.dp(22))); - var islandLp = new android.widget.LinearLayout.LayoutParams(this.dp(104), this.dp(88)); + var islandLp = new android.widget.LinearLayout.LayoutParams(compactWelcome ? this.dp(76) : this.dp(104), compactWelcome ? this.dp(64) : this.dp(88)); islandLp.setMargins(0, 0, this.dp(14), 0); card.addView(island, islandLp); var right = new android.widget.LinearLayout(context); @@ -244,14 +301,14 @@ FloatBallAppWM.prototype.createIslandWelcomeCard = function(parent, statusLabel, var titleMain = new android.widget.TextView(context); titleMain.setText("欢迎回来,岛主"); titleMain.setTextColor(T.text); - titleMain.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 21); + titleMain.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, compactWelcome ? 17 : 21); titleMain.setTypeface(null, android.graphics.Typeface.BOLD); right.addView(titleMain, new android.widget.LinearLayout.LayoutParams(-1, -2)); var titleSub = new android.widget.TextView(context); titleSub.setText("今天也来整理你的小工具吧"); titleSub.setTextColor(T.sub); titleSub.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12); - titleSub.setPadding(0, this.dp(4), 0, this.dp(10)); + titleSub.setPadding(0, this.dp(3), 0, compactWelcome ? this.dp(6) : this.dp(10)); right.addView(titleSub, new android.widget.LinearLayout.LayoutParams(-1, -2)); var statusBar = new android.widget.LinearLayout(context); statusBar.setOrientation(android.widget.LinearLayout.HORIZONTAL); @@ -279,8 +336,8 @@ FloatBallAppWM.prototype.createIslandWelcomeCard = function(parent, statusLabel, statusBar.addView(ok, new android.widget.LinearLayout.LayoutParams(this.dp(24), this.dp(24))); right.addView(statusBar, new android.widget.LinearLayout.LayoutParams(-1, -2)); card.addView(right, new android.widget.LinearLayout.LayoutParams(0, -1, 1)); - var lp = new android.widget.LinearLayout.LayoutParams(-1, this.dp(120)); - lp.setMargins(0, this.dp(8), 0, this.dp(12)); + var lp = new android.widget.LinearLayout.LayoutParams(-1, compactWelcome ? this.dp(92) : this.dp(120)); + lp.setMargins(0, compactWelcome ? this.dp(4) : this.dp(8), 0, compactWelcome ? this.dp(8) : this.dp(12)); parent.addView(card, lp); }; @@ -292,9 +349,17 @@ FloatBallAppWM.prototype.buildSettingsHomePanelView = function() { var T = this.getAnimalIslandTheme(); var cfgTpl = this.state.pendingUserCfg ? this.state.pendingUserCfg : this.config; this.applySettingsTheme(T, isDark, C, cfgTpl); + var spec = this.getSettingsResponsiveSpec ? this.getSettingsResponsiveSpec() : null; var useMonetHome = this.isSettingsMonetTheme ? this.isSettingsMonetTheme(cfgTpl) : false; + var columns = spec ? spec.gridColumnCount : 1; + var useSideBySide = spec && spec.useSideBySide; + var cardRadius = spec ? spec.cardRadius : this.dp(24); + var gap = spec ? spec.cardGap : this.dp(8); + var panel = this.ui.createStyledPanel(this, 16); - try { panel.setBackground(this.ui.createRoundDrawable(T.bg, this.dp(24))); } catch(ePanelBg) {} + try { panel.setBackground(this.ui.createRoundDrawable(T.bg, spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(18) : this.dp(24))); } catch(ePanelBg) {} + panel.setPadding(spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(4) : this.dp(8), spec && spec.isLandscape ? this.dp(2) : this.dp(6), spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(4) : this.dp(8), this.dp(4)); + var statusLabel = "已保存"; var statusValue = "当前生效"; var statusBg = T.card; @@ -302,25 +367,54 @@ FloatBallAppWM.prototype.buildSettingsHomePanelView = function() { var statusValueColor = T.text; try { if (this.state.previewMode) { - statusLabel = "预览中"; - statusValue = "未保存"; - statusBg = T.primarySoft; - statusStroke = T.primaryDeep; - statusValueColor = T.primaryDeep; + statusLabel = "预览中"; statusValue = "未保存"; + statusBg = T.primarySoft; statusStroke = T.primaryDeep; statusValueColor = T.primaryDeep; } else if (this.state.pendingDirty) { - statusLabel = "有修改"; - statusValue = "待保存"; - statusBg = T.primarySoft; - statusStroke = T.primaryDeep; - statusValueColor = T.primaryDeep; + statusLabel = "有修改"; statusValue = "待保存"; + statusBg = T.primarySoft; statusStroke = T.primaryDeep; statusValueColor = T.primaryDeep; } } catch(eStatus) {} - this.createIslandWelcomeCard(panel, statusLabel, statusValue, statusBg, statusStroke, statusValueColor); + + var main = new android.widget.LinearLayout(context); + main.setOrientation(useSideBySide ? android.widget.LinearLayout.HORIZONTAL : android.widget.LinearLayout.VERTICAL); + main.setGravity(android.view.Gravity.CENTER); + main.setPadding(0, 0, 0, 0); + panel.addView(main, new android.widget.LinearLayout.LayoutParams(-1, 0, 1)); + + var leftPane = new android.widget.LinearLayout(context); + leftPane.setOrientation(android.widget.LinearLayout.VERTICAL); + leftPane.setGravity(android.view.Gravity.TOP); + this.createIslandWelcomeCard(leftPane, statusLabel, statusValue, statusBg, statusStroke, statusValueColor); + + if (useSideBySide) { + var hintCard = new android.widget.LinearLayout(context); + hintCard.setOrientation(android.widget.LinearLayout.VERTICAL); + hintCard.setPadding(this.dp(14), this.dp(12), this.dp(14), this.dp(12)); + hintCard.setBackground(this.ui.createStrokeDrawable(T.card, this.withAlpha(T.stroke, isDark ? 0.24 : 0.28), this.dp(1), cardRadius)); + try { hintCard.setElevation(this.dp(1)); } catch(eHintElev) {} + var hintTitle = new android.widget.TextView(context); + hintTitle.setText(useMonetHome ? "当前设置" : "岛屿小提示"); + hintTitle.setTextColor(T.text); + hintTitle.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 14); + hintTitle.setTypeface(null, android.graphics.Typeface.BOLD); + hintCard.addView(hintTitle, new android.widget.LinearLayout.LayoutParams(-1, -2)); + var hintSub = new android.widget.TextView(context); + hintSub.setText(useMonetHome ? "左侧保持状态说明,右侧集中整理设置项。" : "左侧放欢迎与状态,右侧整理工具伙伴、漂浮气球和面板小屋。绿色只做点缀,奶油卡片作为主体。"); + hintSub.setTextColor(T.sub); + hintSub.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 12); + hintSub.setPadding(0, this.dp(6), 0, 0); + hintCard.addView(hintSub, new android.widget.LinearLayout.LayoutParams(-1, -2)); + var hintLp = new android.widget.LinearLayout.LayoutParams(-1, -2); + hintLp.setMargins(0, 0, 0, gap); + leftPane.addView(hintCard, hintLp); + } + var contentCard = new android.widget.LinearLayout(context); contentCard.setOrientation(android.widget.LinearLayout.VERTICAL); - contentCard.setPadding(this.dp(12), this.dp(10), this.dp(12), this.dp(10)); - contentCard.setBackground(this.ui.createStrokeDrawable(T.card, this.withAlpha(T.stroke, isDark ? 0.30 : 0.32), this.dp(1), this.dp(24))); - try { contentCard.setElevation(this.dp(useMonetHome ? 1 : 3)); } catch(eContentElev) {} + contentCard.setPadding(spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(10) : this.dp(12), spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(8) : this.dp(10), spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(10) : this.dp(12), this.dp(10)); + contentCard.setBackground(this.ui.createStrokeDrawable(T.card, this.withAlpha(T.stroke, isDark ? 0.24 : 0.26), this.dp(1), cardRadius)); + try { contentCard.setElevation(this.dp(useMonetHome ? 1 : ((spec && (spec.isExpandedWidth || spec.isWideWidth)) ? 1 : 3))); } catch(eContentElev) {} + var scroll = new android.widget.ScrollView(context); try { scroll.setOverScrollMode(android.view.View.OVER_SCROLL_NEVER); } catch(eOS) { safeLog(null, 'e', "catch " + String(eOS)); } try { scroll.setVerticalScrollBarEnabled(false); } catch(eSB) { safeLog(null, 'e', "catch " + String(eSB)); } @@ -329,64 +423,66 @@ FloatBallAppWM.prototype.buildSettingsHomePanelView = function() { box.setPadding(0, 0, 0, this.dp(8)); scroll.addView(box); scroll.setOnTouchListener(new JavaAdapter(android.view.View.OnTouchListener, { onTouch: function(v, e) { self.touchActivity(); return false; }})); + + function addGridSection(icon, title, filterFn, addSchema) { + self.createSettingsHomeSectionHeader(box, icon, title); + var grid = columns > 1 ? self.createSettingsGridContainer(columns) : new android.widget.LinearLayout(context); + if (columns <= 1) grid.setOrientation(android.widget.LinearLayout.VERTICAL); + box.addView(grid, new android.widget.LinearLayout.LayoutParams(-1, -2)); + if (addSchema) self.createSettingsHomeEntry(grid, useMonetHome ? "高级设置" : "高级蓝图", "编辑设置页结构和高级配置,适合进阶用户", "", function() { self.pushToolAppPage("schema_editor"); }); + return grid; + } + if (useMonetHome) { - this.createSettingsHomeSectionHeader(box, "▦", "工具与配置"); - this.createSettingsHomeEntry(box, "工具", "添加、整理和排序工具入口", "", function() { self.pushToolAppPage("btn_editor"); }); + var g0 = addGridSection("▦", "工具与配置", null, false); + this.createSettingsHomeEntry(g0, "工具", "添加、整理和排序工具入口", "", function() { self.pushToolAppPage("btn_editor"); }); + var defs0 = this.getSettingsGroupDefs(); + for (var a = 0; a < defs0.length; a++) { + (function(d0) { self.createSettingsHomeEntry(g0, d0.title, d0.desc, "", function() { if (self.pushToolAppSettingsGroup) self.pushToolAppSettingsGroup(d0.key); }); })(defs0[a]); + } + var gAdv = addGridSection("⚙", "高级设置", null, true); } else { - this.createSettingsHomeSectionHeader(box, "🧰", "布局与管理"); - this.createSettingsHomeEntry(box, "工具伙伴", "添加、整理和安排你的工具伙伴", "", function() { self.pushToolAppPage("btn_editor"); }); - this.addSettingsHomeDashedDivider(box); - } - var defs = this.getSettingsGroupDefs(); - if (!useMonetHome) this.createSettingsHomeSectionHeader(box, "🎈", "趣味元素"); - for (var i = 0; i < defs.length; i++) { - (function(d) { - if (!useMonetHome && (d.key === "theme" || d.key === "motion" || d.key === "debug")) return; - self.createSettingsHomeEntry(box, d.title, d.desc, "", function() { - if (self.pushToolAppSettingsGroup) self.pushToolAppSettingsGroup(d.key); - }); - })(defs[i]); - } - if (!useMonetHome) { - this.addSettingsHomeDashedDivider(box); - this.createSettingsHomeSectionHeader(box, "👕", "外观与互动"); + var g1 = addGridSection("🧰", "布局与管理", null, false); + this.createSettingsHomeEntry(g1, "工具伙伴", "添加、整理和安排你的工具伙伴", "", function() { self.pushToolAppPage("btn_editor"); }); + var defs = this.getSettingsGroupDefs(); + var g2 = addGridSection("🎈", "趣味元素", null, false); + for (var i = 0; i < defs.length; i++) { + (function(d) { if (d.key === "theme" || d.key === "motion" || d.key === "debug") return; self.createSettingsHomeEntry(g2, d.title, d.desc, "", function() { if (self.pushToolAppSettingsGroup) self.pushToolAppSettingsGroup(d.key); }); })(defs[i]); + } + var g3 = addGridSection("👕", "外观与互动", null, false); for (var j = 0; j < defs.length; j++) { - (function(d2) { - if (d2.key !== "theme" && d2.key !== "motion") return; - self.createSettingsHomeEntry(box, d2.title, d2.desc, "", function() { - if (self.pushToolAppSettingsGroup) self.pushToolAppSettingsGroup(d2.key); - }); - })(defs[j]); + (function(d2) { if (d2.key !== "theme" && d2.key !== "motion") return; self.createSettingsHomeEntry(g3, d2.title, d2.desc, "", function() { if (self.pushToolAppSettingsGroup) self.pushToolAppSettingsGroup(d2.key); }); })(defs[j]); } - this.addSettingsHomeDashedDivider(box); - this.createSettingsHomeSectionHeader(box, "📒", "记录与状态"); + var g4 = addGridSection("📒", "记录与状态", null, false); for (var k = 0; k < defs.length; k++) { - (function(d3) { - if (d3.key !== "debug") return; - self.createSettingsHomeEntry(box, d3.title, d3.desc, "", function() { - if (self.pushToolAppSettingsGroup) self.pushToolAppSettingsGroup(d3.key); - }); - })(defs[k]); + (function(d3) { if (d3.key !== "debug") return; self.createSettingsHomeEntry(g4, d3.title, d3.desc, "", function() { if (self.pushToolAppSettingsGroup) self.pushToolAppSettingsGroup(d3.key); }); })(defs[k]); } - this.createSettingsHomeEntry(box, "高级蓝图", "编辑设置页结构和高级配置,适合进阶用户", "", function() { self.pushToolAppPage("schema_editor"); }); - } else { - this.addSettingsHomeDashedDivider(box); - this.createSettingsHomeSectionHeader(box, "⚙", "高级设置"); - this.createSettingsHomeEntry(box, "高级蓝图", "编辑设置页结构和高级配置,适合进阶用户", "", function() { self.pushToolAppPage("schema_editor"); }); + this.createSettingsHomeEntry(g4, "高级蓝图", "编辑设置页结构和高级配置,适合进阶用户", "", function() { self.pushToolAppPage("schema_editor"); }); } + contentCard.addView(scroll, new android.widget.LinearLayout.LayoutParams(-1, 0, 1)); - panel.addView(contentCard, new android.widget.LinearLayout.LayoutParams(-1, 0, 1)); + + if (useSideBySide) { + var leftLp = new android.widget.LinearLayout.LayoutParams(spec.leftPaneWidth, -1); + leftLp.setMargins(0, 0, gap, 0); + main.addView(leftPane, leftLp); + main.addView(contentCard, new android.widget.LinearLayout.LayoutParams(0, -1, 1)); + } else { + main.addView(leftPane, new android.widget.LinearLayout.LayoutParams(-1, -2)); + main.addView(contentCard, new android.widget.LinearLayout.LayoutParams(-1, 0, 1)); + } + var bottom = new android.widget.LinearLayout(context); bottom.setOrientation(android.widget.LinearLayout.VERTICAL); bottom.setGravity(android.view.Gravity.CENTER); - bottom.setPadding(this.dp(14), this.dp(8), this.dp(14), this.dp(14)); - bottom.setBackground(this.ui.createRoundDrawable(this.withAlpha(T.bg, isDark ? 0.88 : 0.96), this.dp(22))); + bottom.setPadding(this.dp(14), spec && spec.isLandscape ? this.dp(4) : this.dp(8), this.dp(14), spec && spec.isLandscape ? this.dp(8) : this.dp(14)); + bottom.setBackground(this.ui.createRoundDrawable(this.withAlpha(T.bg, isDark ? 0.70 : 0.82), this.dp(20))); var deco = new android.widget.TextView(context); - deco.setText("🌿 ✿ ✿ 🌿"); + deco.setText(spec && (spec.isExpandedWidth || spec.isWideWidth) ? "🌿 ✿ 🌿" : "🌿 ✿ ✿ 🌿"); deco.setGravity(android.view.Gravity.CENTER); - deco.setTextColor(this.withAlpha(T.primaryDeep, isDark ? 0.36 : 0.30)); + deco.setTextColor(this.withAlpha(T.primaryDeep, isDark ? 0.30 : 0.24)); deco.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 10); - bottom.addView(deco, new android.widget.LinearLayout.LayoutParams(-1, this.dp(14))); + if (!(spec && spec.isLandscape)) bottom.addView(deco, new android.widget.LinearLayout.LayoutParams(-1, this.dp(14))); var btnSave = this.ui.createSolidButton(this, useMonetHome ? "保存" : "保存布置", T.primary, T.onPrimary, function() { try { self.touchActivity(); @@ -401,13 +497,13 @@ FloatBallAppWM.prototype.buildSettingsHomePanelView = function() { }); btnSave.setText(useMonetHome ? "保存" : "💾 保存布置"); btnSave.setPadding(this.dp(18), 0, this.dp(18), 0); - try { btnSave.setBackground(this.ui.createStrokeDrawable(T.primary, this.withAlpha(T.primaryDeep, isDark ? 0.26 : 0.18), this.dp(1), this.dp(23))); } catch(eSaveBg) {} - try { btnSave.setElevation(this.dp(2)); } catch(eSaveElev) {} + try { btnSave.setBackground(this.ui.createStrokeDrawable(T.primary, this.withAlpha(T.primaryDeep, isDark ? 0.22 : 0.16), this.dp(1), this.dp(23))); } catch(eSaveBg) {} + try { btnSave.setElevation(this.dp(1)); } catch(eSaveElev) {} var saveLp = new android.widget.LinearLayout.LayoutParams(-1, this.dp(44)); - saveLp.setMargins(this.dp(34), this.dp(4), this.dp(34), 0); + saveLp.setMargins(spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(80) : this.dp(34), this.dp(4), spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(80) : this.dp(34), 0); bottom.addView(btnSave, saveLp); var bottomLp = new android.widget.LinearLayout.LayoutParams(-1, -2); - bottomLp.setMargins(this.dp(2), this.dp(4), this.dp(2), 0); + bottomLp.setMargins(this.dp(2), useSideBySide ? this.dp(2) : this.dp(4), this.dp(2), 0); panel.addView(bottom, bottomLp); return panel; }; @@ -431,10 +527,14 @@ FloatBallAppWM.prototype.buildSettingsGroupPanelView = function() { var bgColor = T.bg; var cardColor = T.card; var textColor = T.text; + var spec = this.getSettingsResponsiveSpec ? this.getSettingsResponsiveSpec() : null; + var columns = spec ? spec.gridColumnCount : 1; + var cardRadius = spec ? spec.cardRadius : this.dp(18); var panel = this.ui.createStyledPanel(this, 16); - try { panel.setBackground(this.ui.createRoundDrawable(T.bg, this.dp(18))); } catch(ePanelBg) {} - var header = this.ui.createStyledHeader(this, 8); + try { panel.setBackground(this.ui.createRoundDrawable(T.bg, spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(16) : this.dp(18))); } catch(ePanelBg) {} + panel.setPadding(spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(4) : this.dp(8), spec && spec.isLandscape ? this.dp(2) : this.dp(6), spec && (spec.isExpandedWidth || spec.isWideWidth) ? this.dp(4) : this.dp(8), this.dp(4)); + var header = this.ui.createStyledHeader(this, spec && spec.isLandscape ? 4 : 8); // 占位 View 顶替标题位置,让右侧按钮靠右 header.addView(this.ui.createSpacer(this)); @@ -523,9 +623,9 @@ FloatBallAppWM.prototype.buildSettingsGroupPanelView = function() { try { scroll.setOverScrollMode(android.view.View.OVER_SCROLL_NEVER); } catch(eOS) { safeLog(null, 'e', "catch " + String(eOS)); } try { scroll.setVerticalScrollBarEnabled(false); } catch(eSB) { safeLog(null, 'e', "catch " + String(eSB)); } - var box = new android.widget.LinearLayout(context); - box.setOrientation(android.widget.LinearLayout.VERTICAL); - box.setPadding(0, this.dp(4), 0, this.dp(4)); + var box = columns > 1 ? this.createSettingsGridContainer(columns) : new android.widget.LinearLayout(context); + if (columns <= 1) box.setOrientation(android.widget.LinearLayout.VERTICAL); + box.setPadding(0, this.dp(4), 0, this.dp(12)); scroll.addView(box); scroll.setOnTouchListener(new JavaAdapter(android.view.View.OnTouchListener, { @@ -540,12 +640,21 @@ FloatBallAppWM.prototype.buildSettingsGroupPanelView = function() { function createCard() { var c = new android.widget.LinearLayout(context); c.setOrientation(android.widget.LinearLayout.VERTICAL); - c.setBackground(self.ui.createStrokeDrawable(cardColor, self.withAlpha(T.stroke, isDark ? 0.28 : 0.46), self.dp(1), self.dp(16))); - try { c.setElevation(self.dp(2)); } catch(e) { safeLog(null, 'e', "catch " + String(e)); } + c.setBackground(self.ui.createStrokeDrawable(cardColor, self.withAlpha(T.stroke, isDark ? 0.22 : 0.30), self.dp(1), cardRadius)); + try { c.setElevation(self.dp((spec && (spec.isExpandedWidth || spec.isWideWidth)) ? 1 : 2)); } catch(e) { safeLog(null, 'e', "catch " + String(e)); } try { c.setClipToOutline(true); } catch(e) { safeLog(null, 'e', "catch " + String(e)); } - var lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); - lp.setMargins(self.dp(2), self.dp(6), self.dp(2), self.dp(6)); - c.setLayoutParams(lp); + if (columns > 1) { + var glp = new android.widget.GridLayout.LayoutParams(); + glp.width = 0; + glp.height = android.widget.GridLayout.LayoutParams.WRAP_CONTENT; + glp.columnSpec = android.widget.GridLayout.spec(android.widget.GridLayout.UNDEFINED, 1, 1); + glp.setMargins(self.dp(6), self.dp(6), self.dp(6), self.dp(8)); + c.setLayoutParams(glp); + } else { + var lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT); + lp.setMargins(self.dp(2), self.dp(6), self.dp(2), self.dp(6)); + c.setLayoutParams(lp); + } // Remove padding to allow items to be full-width (for ripple) c.setPadding(0, 0, 0, self.dp(4)); return c; diff --git a/code/th_15_extra.js b/code/th_15_extra.js index 7a41c55..340be71 100644 --- a/code/th_15_extra.js +++ b/code/th_15_extra.js @@ -621,14 +621,14 @@ FloatBallAppWM.prototype.buildToolAppPreviewBody = function(entry) { body.setPadding(this.dp(6), this.dp(6), this.dp(6), this.dp(8)); body.setBackground(this.ui.createStrokeDrawable(T.bg, this.withAlpha(T.stroke, isDark ? 0.42 : 0.70), this.dp(1), this.dp(26))); try { body.setClipToOutline(true); } catch(eClip) {} - try { body.setElevation(this.dp(12)); } catch (eElev) {} + try { body.setElevation(this.dp((spec && (spec.isExpandedWidth || spec.isWideWidth)) ? 7 : 10)); } catch (eElev) {} var bar = new android.widget.LinearLayout(context); bar.setOrientation(android.widget.LinearLayout.HORIZONTAL); bar.setGravity(android.view.Gravity.CENTER_VERTICAL); - bar.setPadding(this.dp(10), this.dp(10), this.dp(10), this.dp(6)); + bar.setPadding(this.dp(10), (spec && spec.isLandscape) ? this.dp(6) : this.dp(10), this.dp(10), this.dp(6)); bar.setBackground(this.ui.createStrokeDrawable(T.card, this.withAlpha(T.stroke, isDark ? 0.30 : 0.45), this.dp(1), this.dp(20))); - try { bar.setElevation(this.dp(3)); } catch(eBarElev) {} + try { bar.setElevation(this.dp((spec && (spec.isExpandedWidth || spec.isWideWidth)) ? 1 : 2)); } catch(eBarElev) {} var btnBack = this.ui.createFlatButton(this, "‹", T.brown, function() {}); btnBack.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 24); @@ -657,7 +657,7 @@ FloatBallAppWM.prototype.buildToolAppPreviewBody = function(entry) { try { btnClose.setClickable(false); } catch(eRightClick) {} try { btnClose.setBackground(this.ui.createStrokeDrawable(T.primarySoft, this.withAlpha(T.primaryDeep, isDark ? 0.30 : 0.22), this.dp(1), this.dp(18))); } catch(eRightBg) {} bar.addView(btnClose, new android.widget.LinearLayout.LayoutParams(this.dp(104), this.dp(38))); - var barLp = new android.widget.LinearLayout.LayoutParams(-1, this.dp(56)); + var barLp = new android.widget.LinearLayout.LayoutParams(-1, topBarHeight); barLp.setMargins(this.dp(8), this.dp(8), this.dp(8), this.dp(4)); body.addView(bar, barLp); @@ -667,7 +667,7 @@ FloatBallAppWM.prototype.buildToolAppPreviewBody = function(entry) { try { raw.setElevation(0); } catch (eEl) {} host.addView(raw, new android.widget.FrameLayout.LayoutParams(-1, -1)); var hostLp = new android.widget.LinearLayout.LayoutParams(-1, 0, 1); - hostLp.setMargins(this.dp(6), 0, this.dp(6), this.dp(6)); + hostLp.setMargins((spec && (spec.isExpandedWidth || spec.isWideWidth)) ? this.dp(4) : this.dp(6), 0, (spec && (spec.isExpandedWidth || spec.isWideWidth)) ? this.dp(4) : this.dp(6), (spec && (spec.isExpandedWidth || spec.isWideWidth)) ? this.dp(4) : this.dp(6)); body.addView(host, hostLp); return body; } catch (e) { @@ -928,6 +928,34 @@ FloatBallAppWM.prototype.showToolAppScreenBackStrips = function() { return false; }; +FloatBallAppWM.prototype.getToolAppResponsiveSpec = function() { + var sw = Math.max(1, Number(this.state.screen && this.state.screen.w || 0)); + var sh = Math.max(1, Number(this.state.screen && this.state.screen.h || 0)); + if (sw <= 1 || sh <= 1) { + try { var ss = this.getScreenSizePx(); sw = ss.w; sh = ss.h; } catch (eScreen) {} + } + var isLandscape = sw > sh; + var isCompactWidth = sw < this.dp(600); + var isMediumWidth = sw >= this.dp(600) && sw < this.dp(840); + var isExpandedWidth = sw >= this.dp(840) && sw < this.dp(1200); + var isWideWidth = sw >= this.dp(1200); + return { + screenW: sw, screenH: sh, isLandscape: isLandscape, + isCompactWidth: isCompactWidth, isMediumWidth: isMediumWidth, + isExpandedWidth: isExpandedWidth, isWideWidth: isWideWidth, + contentMaxWidth: isWideWidth ? this.dp(1080) : (isExpandedWidth ? this.dp(960) : (isMediumWidth ? this.dp(680) : this.dp(560))), + gridColumnCount: (isExpandedWidth || isWideWidth) ? 2 : 1, + useSideBySide: isLandscape && (isExpandedWidth || isWideWidth), + leftPaneWidth: isWideWidth ? this.dp(340) : this.dp(300), + outerRadius: (isExpandedWidth || isWideWidth) ? this.dp(30) : this.dp(26), + cardRadius: (isExpandedWidth || isWideWidth) ? this.dp(20) : this.dp(24), + itemRadius: (isExpandedWidth || isWideWidth) ? this.dp(16) : this.dp(18), + shellPadding: (isExpandedWidth || isWideWidth) ? this.dp(4) : this.dp(6), + cardGap: (isExpandedWidth || isWideWidth) ? this.dp(14) : this.dp(8), + topBarHeight: (isLandscape && isCompactWidth) ? this.dp(50) : this.dp(56) + }; +}; + FloatBallAppWM.prototype.buildToolAppShell = function(contentView, title, canBack) { var self = this; var isDark = this.isDarkTheme(); @@ -935,22 +963,26 @@ FloatBallAppWM.prototype.buildToolAppShell = function(contentView, title, canBac var T = this.getAnimalIslandTheme(); var cfgTpl = this.state.pendingUserCfg ? this.state.pendingUserCfg : this.config; try { if (this.applySettingsTheme) this.applySettingsTheme(T, isDark, C, cfgTpl); } catch(eTheme) { safeLog(null, 'e', "catch " + String(eTheme)); } + var spec = this.getToolAppResponsiveSpec ? this.getToolAppResponsiveSpec() : null; + var shellPad = spec ? spec.shellPadding : this.dp(6); + var outerRadius = spec ? spec.outerRadius : this.dp(26); + var topBarHeight = spec ? spec.topBarHeight : this.dp(56); var root = new android.widget.FrameLayout(context); var body = new android.widget.LinearLayout(context); body.setOrientation(android.widget.LinearLayout.VERTICAL); // 外层薄荷容器本身就是整张“岛屿设置”卡片:四角统一圆角,并给底部留出完整收口。 - body.setPadding(this.dp(6), this.dp(6), this.dp(6), this.dp(8)); - body.setBackground(this.ui.createStrokeDrawable(T.bg, this.withAlpha(T.stroke, isDark ? 0.42 : 0.70), this.dp(1), this.dp(26))); + body.setPadding(shellPad, shellPad, shellPad, shellPad); + body.setBackground(this.ui.createStrokeDrawable(T.bg, this.withAlpha(T.stroke, isDark ? 0.30 : 0.46), this.dp(1), outerRadius)); try { body.setClipToOutline(true); } catch(eClip) {} - try { body.setElevation(this.dp(12)); } catch(eElev) { safeLog(null, 'e', "catch " + String(eElev)); } + try { body.setElevation(this.dp((spec && (spec.isExpandedWidth || spec.isWideWidth)) ? 7 : 10)); } catch(eElev) { safeLog(null, 'e', "catch " + String(eElev)); } root.addView(body, new android.widget.FrameLayout.LayoutParams(-1, -1)); var bar = new android.widget.LinearLayout(context); bar.setOrientation(android.widget.LinearLayout.HORIZONTAL); bar.setGravity(android.view.Gravity.CENTER_VERTICAL); - bar.setPadding(this.dp(10), this.dp(10), this.dp(10), this.dp(6)); + bar.setPadding(this.dp(10), (spec && spec.isLandscape) ? this.dp(6) : this.dp(10), this.dp(10), this.dp(6)); bar.setBackground(this.ui.createStrokeDrawable(T.card, this.withAlpha(T.stroke, isDark ? 0.30 : 0.45), this.dp(1), this.dp(20))); - try { bar.setElevation(this.dp(3)); } catch(eBarElev) {} + try { bar.setElevation(this.dp((spec && (spec.isExpandedWidth || spec.isWideWidth)) ? 1 : 2)); } catch(eBarElev) {} var btnBack = this.ui.createFlatButton(this, "‹", T.brown, function() { self.popToolAppPage("topbar"); @@ -987,7 +1019,7 @@ FloatBallAppWM.prototype.buildToolAppShell = function(contentView, title, canBac btnClose.setPadding(this.dp(10), 0, this.dp(10), 0); try { btnClose.setBackground(this.ui.createStrokeDrawable(T.primarySoft, this.withAlpha(T.primaryDeep, isDark ? 0.30 : 0.22), this.dp(1), this.dp(18))); } catch(eRightBg) {} bar.addView(btnClose, new android.widget.LinearLayout.LayoutParams(this.dp(104), this.dp(38))); - var barLp = new android.widget.LinearLayout.LayoutParams(-1, this.dp(56)); + var barLp = new android.widget.LinearLayout.LayoutParams(-1, topBarHeight); barLp.setMargins(this.dp(8), this.dp(8), this.dp(8), this.dp(4)); body.addView(bar, barLp); @@ -998,7 +1030,7 @@ FloatBallAppWM.prototype.buildToolAppShell = function(contentView, title, canBac host.addView(contentView, new android.widget.FrameLayout.LayoutParams(-1, -1)); } var hostLp = new android.widget.LinearLayout.LayoutParams(-1, 0, 1); - hostLp.setMargins(this.dp(6), 0, this.dp(6), this.dp(6)); + hostLp.setMargins((spec && (spec.isExpandedWidth || spec.isWideWidth)) ? this.dp(4) : this.dp(6), 0, (spec && (spec.isExpandedWidth || spec.isWideWidth)) ? this.dp(4) : this.dp(6), (spec && (spec.isExpandedWidth || spec.isWideWidth)) ? this.dp(4) : this.dp(6)); body.addView(host, hostLp); try { @@ -1104,57 +1136,37 @@ FloatBallAppWM.prototype.calculateToolAppLayout = function(shell) { if (sw <= 1 || sh <= 1) { try { var ss = this.getScreenSizePx(); sw = ss.w; sh = ss.h; } catch (eScreen) {} } + var spec = this.getToolAppResponsiveSpec ? this.getToolAppResponsiveSpec() : null; + var isLandscape = spec ? spec.isLandscape : (sw > sh); var shortSide = Math.min(sw, sh); var longSide = Math.max(sw, sh); - var isLandscape = sw > sh; - - var marginX = this.dp(12); - var marginTop = this.dp(14); - var marginBottom = this.dp(14); - var targetW; - var targetH; - - if (shortSide < this.dp(420)) { - // 小屏/分屏:接近全屏,避免内容被裁剪。 - marginX = this.dp(8); - marginTop = this.dp(8); - marginBottom = this.dp(8); - targetW = sw - marginX * 2; - targetH = sh - marginTop - marginBottom; - } else if (shortSide >= this.dp(720)) { - // 平板/大屏:不要铺满,保持卡片阅读宽度。 - marginX = this.dp(28); - marginTop = this.dp(28); - marginBottom = this.dp(28); - targetW = Math.min(this.dp(680), Math.floor(sw * (isLandscape ? 0.56 : 0.72))); - targetH = Math.min(this.dp(780), sh - marginTop - marginBottom); - } else if (isLandscape) { - // 手机横屏:高度优先,宽度适当收窄。 - marginX = this.dp(18); - marginTop = this.dp(12); - marginBottom = this.dp(12); - targetW = Math.min(this.dp(620), Math.floor(sw * 0.68)); - targetH = sh - marginTop - marginBottom; + var marginX = this.dp(12), marginTop = this.dp(14), marginBottom = this.dp(14); + var targetW, targetH; + if (spec && (spec.isCompactWidth || shortSide < this.dp(420))) { + marginX = this.dp(isLandscape ? 8 : 10); marginTop = this.dp(isLandscape ? 6 : 14); marginBottom = this.dp(isLandscape ? 6 : 14); + targetW = Math.min(spec.contentMaxWidth, sw - marginX * 2); + targetH = isLandscape ? (sh - marginTop - marginBottom) : Math.min(Math.floor(sh * 0.92), sh - marginTop - marginBottom); + } else if (spec && spec.isMediumWidth) { + marginX = this.dp(18); marginTop = this.dp(isLandscape ? 10 : 18); marginBottom = this.dp(isLandscape ? 10 : 18); + targetW = Math.min(spec.contentMaxWidth, sw - marginX * 2); targetH = sh - marginTop - marginBottom; + } else if (spec && (spec.isExpandedWidth || shortSide >= this.dp(720))) { + marginX = this.dp(22); marginTop = this.dp(isLandscape ? 18 : 24); marginBottom = this.dp(isLandscape ? 18 : 24); + targetW = Math.min(spec.contentMaxWidth, sw - marginX * 2); targetH = sh - marginTop - marginBottom; } else { - // 手机竖屏:左右留少量边距,高度随屏幕增长。 - marginX = this.dp(12); - marginTop = this.dp(18); - marginBottom = this.dp(18); - targetW = Math.min(this.dp(560), sw - marginX * 2); - targetH = Math.min(Math.floor(sh * 0.90), sh - marginTop - marginBottom); + marginX = this.dp(30); marginTop = this.dp(24); marginBottom = this.dp(24); + targetW = Math.min(spec ? spec.contentMaxWidth : this.dp(1080), sw - marginX * 2); + targetH = Math.min(this.dp(860), sh - marginTop - marginBottom); } - targetW = Math.max(this.dp(300), Math.min(targetW, sw - marginX * 2)); - targetH = Math.max(this.dp(360), Math.min(targetH, sh - marginTop - marginBottom)); - + targetH = Math.max(this.dp(320), Math.min(targetH, sh - marginTop - marginBottom)); var x = Math.floor((sw - targetW) / 2); var y = Math.floor((sh - targetH) / 2); if (x < marginX) x = marginX; if (y < marginTop) y = marginTop; if (x + targetW > sw - marginX) x = Math.max(0, sw - marginX - targetW); if (y + targetH > sh - marginBottom) y = Math.max(0, sh - marginBottom - targetH); - - return { width: targetW, height: targetH, x: x, y: y, marginX: marginX, marginTop: marginTop, marginBottom: marginBottom, isLandscape: isLandscape, shortSide: shortSide, longSide: longSide }; + if (spec) this.state.toolAppResponsiveSpec = spec; + return { width: targetW, height: targetH, x: x, y: y, marginX: marginX, marginTop: marginTop, marginBottom: marginBottom, isLandscape: isLandscape, shortSide: Math.min(sw, sh), longSide: Math.max(sw, sh) }; }; FloatBallAppWM.prototype.showToolApp = function(route, resetStack) { diff --git a/manifest.json b/manifest.json index 7bc7817..bf88ca0 100644 --- a/manifest.json +++ b/manifest.json @@ -54,12 +54,12 @@ "size": 21198 }, "th_14_panels.js": { - "sha256": "185d7ed9d85ffa7e31b70abbd425c0d79d6a435a4c5cb6dc7610d942fdc0fc87", - "size": 262019 + "sha256": "a6931bf609b2c0c4635c6a856355e35854995c6c45e9b7518a7addcea2e72446", + "size": 270273 }, "th_15_extra.js": { - "sha256": "1aa3bbe9c99e047cfaf663f17501d87bba1b5810a8002e66bca1f6ea605371a4", - "size": 99378 + "sha256": "bdb963bdd6b1b5a7b57f6f55f90a3f022cc2df925765f0195aa6f033efbbc7de", + "size": 102108 }, "th_16_entry.js": { "sha256": "6c59d9891cd010647f84c3db93f1cf95c7bbfb758470ea21044bf72eb8ff73d1", @@ -68,5 +68,5 @@ }, "keyId": "toolhub-targets-2026-rsa3072", "schema": 2, - "version": 20260521161017 + "version": 20260521163950 } diff --git a/manifest.sig b/manifest.sig index 6f9dcf6..694efcd 100644 --- a/manifest.sig +++ b/manifest.sig @@ -1 +1 @@ -OmNJawXdMOyamvJ6qAytud+LHrHchf22fFqWsU6p2UVUavGCGsv4XbNcmvDl7dT85Q9kOT4Qpb68mE8pnYQ3foQ9wYToD2aEooVnjwpCoX5JJmZwxdyR2PQhfgY7e2GNYJ6IubHweZTRZsRYKnMxlA5x8EnaF5kvK+YoCt0dulaMVQS0KK3tpuwlb13WxA8C1256hrGQaZ0R4lptEsOqjRB1r+lk3r27omHEI4rwcxrHmqJ19aOTLKP/9d8xImg4HAI/qeaLDDpfyKkKhQBl4TCp5Sj0KO8Noy48dSbsW7QS5VKKugo0unA5rBKch1QliCNiHF6moK8nVERvAHQLPHbpJvQI78E8rQW0Q632FZUalLOURldsTizsCr9YIZTeB6xumY8h5Ufevpnz9xNmyqUvmvQ8tZ4D5+qn14FngT+WoK6y65DTWjA37UICx5pqqLA4KRyGKuTHDXpaHurzArN4B1OnnDvqgru0GnpYWgfJZTnLBqqpPlBcS56lvYXo +TQXYAx13IeX2RQjgyTYvVvrEBsqFXbjHcdjLPSaydbwscqXirOrPHNR08ySB9MLA0ka/nsr1wra2u3jgQUEWaIIJPWwMI3FmPAVs6HUmcfBHg/rkLbQcqYRxzeVFN16Uq2TMn0x1J8YXarnTxUksRq+WEzoog+FqekNBqKyEMl4OROHO34pexr4FyC12x6Y8c31fV4oLXrIWsXvaR44LF3ki6/h8hh8bvVN7ax8YKryOpkumC+bSw2mL5DwLcEBDEE53GZAp7NAk77BG05SdsklQDXeJuQ8tqsaiL1PlmeUHNE/WyZShGPahe58OqKs68iN02b7W3DYE+qT8ARshPhnStk6OA+hm1bWJL49ZMUxaX4ZCMXLW9Hv7ogu0P0N27MM6CFMtGWV1IBdVE89ywKrfrGLGx3G/J3ScCsPlcIbpqHvn65g0NsMD8EI7VA0KuTMzT/DIuaXxcQ/sC0elgy8yVMcb9bWuGA+oRumaUbr3kZE5BmlanD3RlemtK3Rh