{ "actions": [{ "@type": "type.googleapis.com/ExecuteJS", "expression": "// 快捷app - 应用快捷启动器\n// 来源: xin-blog.com\n\n(function () {\n\n var ctx \u003d null;\n try { ctx \u003d context; } catch (e) { ctx \u003d null; }\n if (!ctx) return JSON.stringify({ ok: 0, err: \"context is null\" });\n\n var ENABLE_SCAN_APPS \u003d true;\n var ENABLE_LOAD_ICONS \u003d true;\n\n var FIRST_SCREEN_PRELOAD \u003d true;\n var FIRST_SCREEN_COUNT \u003d 12;\n\n\n // 更快显示窗口:先显示空面板,再后台扫描与填充数据\n var FAST_STARTUP_SHOW_PANEL_EARLY \u003d true;\n\n // 两阶段冻结态同步:先用 ai.enabled 快速估算,再后台补齐真实冻结状态\n var FAST_STARTUP_FROZEN_SYNC \u003d true;\n var FROZEN_SYNC_DELAY_MS \u003d 260;\n\n // 面板入场动画(仅 UI 线程)\n var SHOW_PANEL_ANIM \u003d true;\n var SHOW_PANEL_ANIM_MS \u003d 180;\n var SHOW_PANEL_ANIM_SCALE_FROM \u003d 0.92;\n\n var PRELOAD_COUNT \u003d 24;\n var PRELOAD_DELAY_MS \u003d 20;\n\n var PIN_STORE_MAX \u003d 200;\n\n\n var COLS \u003d 5;\n var ICON_DP \u003d 52;\n var GAP_DP \u003d 4;\n var TEXT_SP \u003d 11;\n\n var WIN_W_DP \u003d 400;\n var WIN_H_DP \u003d 420;\n\n var DRAG_LONGPRESS_MS \u003d 260;\n var DRAG_SLOP_DP \u003d 6;\n\n // 时间阈值常量\n var LONG_PRESS_THRESHOLD_MS \u003d 520;\n var DOUBLE_CLICK_INTERVAL_MS \u003d 300;\n var TAP_LOCK_DURATION_MS \u003d 220;\n\n // UI尺寸常量\n var WINDOW_MARGIN_DP \u003d 8;\n var SETTINGS_WIDTH_MARGIN_DP \u003d 30;\n var SETTINGS_MIN_WIDTH_DP \u003d 300;\n var SETTINGS_MAX_WIDTH_DP \u003d 760;\n var SETTINGS_MIN_HEIGHT_DP \u003d 260;\n var SETTINGS_MARGIN_BOTTOM_DP \u003d 40;\n\n // 动画常量\n var ANIM_SCALE_FROM \u003d 0.92;\n var ANIM_DURATION_MS \u003d 180;\n\n // 搜索相关\n var SEARCH_DELAY_MS \u003d 150;\n var MAX_TEXT_LENGTH \u003d 5000;\n\n // 布局间距\n var PADDING_SMALL \u003d 4;\n var PADDING_MEDIUM \u003d 8;\n var PADDING_LARGE \u003d 12;\n var PADDING_XLARGE \u003d 16;\n var PADDING_XXLARGE \u003d 24;\n\n // 窗口边距\n var WINDOW_MARGIN_DP \u003d 8;\n\n // 设置面板尺寸\n var SETTINGS_WIDTH_MARGIN_DP \u003d 30;\n var SETTINGS_MIN_WIDTH_DP \u003d 300;\n var SETTINGS_MAX_WIDTH_DP \u003d 760;\n var SETTINGS_HEIGHT_RATIO \u003d 0.85;\n\n\n var KEY_LAST_ERR \u003d \"shortx_appgrid_last_err_v2f\";\n var KEY_LAST_LAUNCH_FAIL \u003d \"shortx_appgrid_last_launch_fail_v2f\";\n var KEY_POS \u003d \"shortx_appgrid_pos_v2f\";\n var KEY_TAB \u003d \"shortx_appgrid_tab_v2f\";\n var KEY_VISMAP \u003d \"shortx_appgrid_vismap_v2f\";\n var KEY_PINNED \u003d \"shortx_appgrid_pinned_v2f\";\n\n // 颜色系统\n var Colors \u003d {\n // 主色调 - 活力蓝紫\n primary: Packages.android.graphics.Color.parseColor(\"#7C4DFF\"),\n onPrimary: Packages.android.graphics.Color.parseColor(\"#FFFFFF\"),\n primaryContainer: Packages.android.graphics.Color.parseColor(\"#E8DDFF\"),\n\n // 次色调 - 柔和蓝\n secondary: Packages.android.graphics.Color.parseColor(\"#5C6BC0\"),\n onSecondary: Packages.android.graphics.Color.parseColor(\"#FFFFFF\"),\n secondaryContainer: Packages.android.graphics.Color.parseColor(\"#DEE0FF\"),\n onSecondaryContainer: Packages.android.graphics.Color.parseColor(\"#12144B\"),\n\n // 表面颜色\n surface: Packages.android.graphics.Color.parseColor(\"#2D2D3A\"),\n onSurface: Packages.android.graphics.Color.parseColor(\"#FFFFFF\"),\n surfaceVariant: Packages.android.graphics.Color.parseColor(\"#3D3D4D\"),\n onSurfaceVariant: Packages.android.graphics.Color.parseColor(\"#E0E0E0\"),\n\n // 背景\n background: Packages.android.graphics.Color.parseColor(\"#1A1A23\"),\n onBackground: Packages.android.graphics.Color.parseColor(\"#FFFFFF\"),\n\n // 轮廓\n outline: Packages.android.graphics.Color.parseColor(\"#6B6B80\"),\n outlineVariant: Packages.android.graphics.Color.parseColor(\"#4A4A5A\"),\n\n // 玻璃拟态背景\n scrim: Packages.android.graphics.Color.parseColor(\"#CC000000\"),\n\n // 卡片背景\n cardBackground: Packages.android.graphics.Color.parseColor(\"#454555\"),\n cardBackgroundFrozen: Packages.android.graphics.Color.parseColor(\"#353545\"),\n\n // Tab栏背景\n tabBackground: Packages.android.graphics.Color.parseColor(\"#252532\"),\n tabSelected: Packages.android.graphics.Color.parseColor(\"#7C4DFF\"),\n tabSelectedText: Packages.android.graphics.Color.parseColor(\"#FFFFFF\"),\n tabUnselectedText: Packages.android.graphics.Color.parseColor(\"#B0B0C0\"),\n };\n\n // dp/sp 工具函数 - 缓存 DisplayMetrics 提高性能\n var _cachedDisplayMetrics \u003d null;\n var _cachedDensity \u003d 0;\n var _cachedScaledDensity \u003d 0;\n\n function getDisplayMetrics() {\n if (!_cachedDisplayMetrics) {\n _cachedDisplayMetrics \u003d ctx.getResources().getDisplayMetrics();\n _cachedDensity \u003d _cachedDisplayMetrics.density;\n _cachedScaledDensity \u003d _cachedDisplayMetrics.scaledDensity;\n }\n return _cachedDisplayMetrics;\n }\n\n function dp(v) {\n if (_cachedDensity \u003d\u003d\u003d 0) getDisplayMetrics();\n return Math.floor(v * _cachedDensity + 0.5);\n }\n function sp(v) {\n if (_cachedScaledDensity \u003d\u003d\u003d 0) getDisplayMetrics();\n return Math.floor(v * _cachedScaledDensity + 0.5);\n }\n\n // 圆角大小\n var CORNER_SMALL \u003d dp(4);\n var CORNER_MEDIUM \u003d dp(8);\n var CORNER_LARGE \u003d dp(12);\n var CORNER_XLARGE \u003d dp(16);\n var CORNER_FULL \u003d dp(28);\n\n // 缓存常用的 Drawable,避免重复创建\n var _drawableCache \u003d {};\n function getCachedRoundedDrawable(color, radius) {\n var key \u003d color + \"_\" + radius;\n if (!_drawableCache[key]) {\n _drawableCache[key] \u003d createRoundedDrawable(color, radius);\n }\n return _drawableCache[key];\n }\n\n // 圆角工具函数\n function createRoundedDrawable(color, radius) {\n var drawable \u003d new android.graphics.drawable.GradientDrawable();\n drawable.setShape(android.graphics.drawable.GradientDrawable.RECTANGLE);\n drawable.setColor(color);\n drawable.setCornerRadius(radius);\n return drawable;\n }\n\n // 创建按钮背景(主色调,带圆角)\n function createButtonPrimaryDrawable() {\n return createRoundedDrawable(Colors.primary, CORNER_FULL);\n }\n\n var TAG_HOLDER \u003d 0x10000001;\n var TAG_PKG \u003d 0x10000002;\n var TAG_HOLDER_POSITION \u003d 0x10000003; // 用于追踪图标位置,防止视图重用导致图标错位\n\n var TAG_ROW_HOLDER \u003d 0x20000001;\n var TAG_ROW_PKG \u003d 0x20000002;\n\n\n var TAB_NORMAL \u003d 0;\n var TAB_FROZEN \u003d 1;\n\n\n var HandlerThread \u003d Packages.android.os.HandlerThread;\n var Handler \u003d Packages.android.os.Handler;\n\n var Gravity \u003d Packages.android.view.Gravity;\n var LayoutParams \u003d Packages.android.view.WindowManager.LayoutParams;\n var MotionEvent \u003d Packages.android.view.MotionEvent;\n\n var FrameLayout \u003d Packages.android.widget.FrameLayout;\n var LinearLayout \u003d Packages.android.widget.LinearLayout;\n var GridView \u003d Packages.android.widget.GridView;\n var GridLayout \u003d Packages.android.widget.GridLayout;\n var ListView \u003d Packages.android.widget.ListView;\n var BaseAdapter \u003d Packages.android.widget.BaseAdapter;\n var ImageView \u003d Packages.android.widget.ImageView;\n var TextView \u003d Packages.android.widget.TextView;\n var CheckBox \u003d Packages.android.widget.CheckBox;\n\n var Color \u003d Packages.android.graphics.Color;\n var ColorDrawable \u003d Packages.android.graphics.drawable.ColorDrawable;\n\n var TextUtils \u003d Packages.android.text.TextUtils;\n var Settings \u003d Packages.android.provider.Settings;\n var Intent \u003d Packages.android.content.Intent;\n var Uri \u003d Packages.android.net.Uri;\n var SystemClock \u003d Packages.android.os.SystemClock;\n\n var LruCache \u003d Packages.android.util.LruCache;\n var ClipData \u003d Packages.android.content.ClipData;\n\n var ReflectArray \u003d java.lang.reflect.Array;\n var ObjectClass \u003d java.lang.Object;\n\n var PM \u003d Packages.android.content.pm.PackageManager;\n\n var wm \u003d ctx.getSystemService(Packages.android.content.Context.WINDOW_SERVICE);\n var pm \u003d ctx.getPackageManager();\n\n\n function screenW() { return ctx.getResources().getDisplayMetrics().widthPixels; }\n function screenH() { return ctx.getResources().getDisplayMetrics().heightPixels; }\n function clamp(n, a, b) {\n if (n \u003c a) return a;\n if (n \u003e b) return b;\n return n;\n }\n\n // 错误处理包装器\n function safe(tag, fn) {\n try {\n return fn();\n } catch (e) {\n try {\n Settings.Global.putString(\n ctx.getContentResolver(),\n KEY_LAST_ERR,\n JSON.stringify({ tag: String(tag), err: String(e), ts: java.lang.System.currentTimeMillis() })\n );\n } catch (e2) {}\n return null;\n }\n }\n\n\n var uiHt \u003d new HandlerThread(\"SX-WM-APPGRID-V2F-HT\");\n uiHt.start();\n var uiHandler \u003d new Handler(uiHt.getLooper());\n\n var wkHt \u003d new HandlerThread(\"SX-WM-APPGRID-V2F-WK\");\n wkHt.start();\n var wkHandler \u003d new Handler(wkHt.getLooper());\n\n // 状态管理\n var closing \u003d 0;\n var lastCloseTapTs \u003d 0;\n\n function getClosing() {\n return safe(\"getClosing\", function() {\n // Rhino 不支持 synchronized 语法,使用简单变量访问\n return closing;\n }) || 0;\n }\n\n function setClosing(val) {\n return safe(\"setClosing\", function() {\n // Rhino 不支持 synchronized 语法,使用简单变量赋值\n closing \u003d val;\n return 1;\n }) || 0;\n }\n\n // 持久化存储\n function loadPosFromSettings() {\n return safe(\"loadPos\", function () {\n var s \u003d Settings.Global.getString(ctx.getContentResolver(), KEY_POS);\n if (!s) return null;\n var obj \u003d null;\n try { obj \u003d JSON.parse(String(s)); } catch (e) { obj \u003d null; }\n if (!obj) return null;\n if (typeof obj.x !\u003d\u003d \"number\" || typeof obj.y !\u003d\u003d \"number\") return null;\n return { x: obj.x, y: obj.y };\n });\n }\n\n function savePosToSettings(x, y) {\n safe(\"savePos\", function () {\n var payload \u003d { ts: java.lang.System.currentTimeMillis(), x: x, y: y };\n Settings.Global.putString(ctx.getContentResolver(), KEY_POS, JSON.stringify(payload));\n return 1;\n });\n }\n\n function loadTabFromSettings() {\n return safe(\"loadTab\", function () {\n var s \u003d Settings.Global.getString(ctx.getContentResolver(), KEY_TAB);\n if (!s) return TAB_NORMAL;\n var n \u003d parseInt(String(s), 10);\n if (n !\u003d\u003d TAB_FROZEN) n \u003d TAB_NORMAL;\n return n;\n }) || TAB_NORMAL;\n }\n\n function saveTabToSettings(tab) {\n safe(\"saveTab\", function () {\n Settings.Global.putString(ctx.getContentResolver(), KEY_TAB, String(tab));\n return 1;\n });\n }\n\n function loadVisMap() {\n return safe(\"loadVisMap\", function () {\n var s \u003d Settings.Global.getString(ctx.getContentResolver(), KEY_VISMAP);\n if (!s) return {};\n var obj \u003d null;\n try { obj \u003d JSON.parse(String(s)); } catch (e) { obj \u003d null; }\n if (!obj || !obj.map) return {};\n return obj.map;\n }) || {};\n }\n\n function saveVisMap() {\n safe(\"saveVisMap\", function () {\n var payload \u003d { ts: java.lang.System.currentTimeMillis(), map: visMap };\n Settings.Global.putString(ctx.getContentResolver(), KEY_VISMAP, JSON.stringify(payload));\n return 1;\n });\n }\n\n function isVisiblePkg(pkg) {\n if (!pkg) return false;\n \n // 检查 visMap 是否为空(新用户首次使用)\n var isEmpty \u003d true;\n for (var key in visMap) {\n if (visMap.hasOwnProperty(key)) {\n isEmpty \u003d false;\n break;\n }\n }\n // 如果是新用户(visMap为空),默认显示所有应用\n if (isEmpty) return true;\n \n var v \u003d visMap[pkg];\n // 只有明确设置为 1 才显示,默认不显示\n if (v \u003d\u003d\u003d 1) return true;\n if (v \u003d\u003d\u003d \"1\") return true;\n return false;\n }\n\n function setVisiblePkg(pkg, visible) {\n if (!pkg) return;\n visMap[pkg] \u003d visible ? 1 : 0;\n saveVisMap();\n }\n\n function loadPinned() {\n return safe(\"loadPinned\", function () {\n var s \u003d Settings.Global.getString(ctx.getContentResolver(), KEY_PINNED);\n if (!s) return [];\n var obj \u003d null;\n try { obj \u003d JSON.parse(String(s)); } catch (e) { obj \u003d null; }\n if (!obj || !obj.list || !(obj.list instanceof Array)) return [];\n\n var out \u003d [];\n for (var i \u003d 0; i \u003c obj.list.length; i++) {\n var p \u003d String(obj.list[i]);\n if (p \u0026\u0026 p !\u003d\u003d \"null\" \u0026\u0026 p !\u003d\u003d \"undefined\") out.push(p);\n if (out.length \u003e\u003d PIN_STORE_MAX) break;\n }\n return out;\n }) || [];\n }\n\n function savePinned() {\n safe(\"savePinned\", function () {\n var out \u003d [];\n for (var i \u003d 0; i \u003c pinned.length; i++) {\n var p \u003d String(pinned[i]);\n if (p \u0026\u0026 p !\u003d\u003d \"null\" \u0026\u0026 p !\u003d\u003d \"undefined\") out.push(p);\n if (out.length \u003e\u003d PIN_STORE_MAX) break;\n }\n pinned \u003d out;\n\n var payload \u003d { ts: java.lang.System.currentTimeMillis(), list: out };\n Settings.Global.putString(ctx.getContentResolver(), KEY_PINNED, JSON.stringify(payload));\n return 1;\n });\n }\n\n\n function pickFlag(name) {\n return safe(\"pickFlag:\" + name, function () {\n var v \u003d PM[name];\n if (typeof v \u003d\u003d\u003d \"number\") return v;\n return 0;\n }) || 0;\n }\n\n function buildInstalledMatchFlags() {\n var f \u003d 0;\n f |\u003d pickFlag(\"MATCH_DISABLED_COMPONENTS\");\n f |\u003d pickFlag(\"MATCH_DISABLED_UNTIL_USED_COMPONENTS\");\n f |\u003d pickFlag(\"MATCH_UNINSTALLED_PACKAGES\");\n f |\u003d pickFlag(\"MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS\");\n f |\u003d pickFlag(\"MATCH_ALL\");\n f |\u003d pickFlag(\"GET_DISABLED_COMPONENTS\");\n return f;\n }\n\n var INSTALLED_MATCH_FLAGS \u003d buildInstalledMatchFlags();\n\n\n // 图标管理(阿然博客风格 - 简洁高效)\n // 使用 Bitmap 缓存避免 Drawable 共享状态问题\n var iconCache \u003d {};\n var MAX_CACHE_SIZE \u003d 50;\n\n // 将 Drawable 转换为 Bitmap 缓存 - 优化版本\n var _iconDp \u003d 0;\n var _Bitmap \u003d null;\n var _BitmapConfig \u003d null;\n var _Canvas \u003d null;\n\n function cacheDrawable(pkg, drawable) {\n try {\n if (!drawable) return;\n\n // 缓存常用类引用\n if (!_Bitmap) {\n _Bitmap \u003d Packages.android.graphics.Bitmap;\n _BitmapConfig \u003d _Bitmap.Config.ARGB_8888;\n _Canvas \u003d Packages.android.graphics.Canvas;\n _iconDp \u003d dp(ICON_DP);\n }\n\n // 检查是否已缓存\n if (iconCache[pkg]) return;\n\n var width \u003d drawable.getIntrinsicWidth();\n var height \u003d drawable.getIntrinsicHeight();\n if (width \u003c\u003d 0 || height \u003c\u003d 0) {\n width \u003d _iconDp;\n height \u003d _iconDp;\n }\n\n var bitmap \u003d _Bitmap.createBitmap(width, height, _BitmapConfig);\n var canvas \u003d new _Canvas(bitmap);\n drawable.setBounds(0, 0, width, height);\n drawable.draw(canvas);\n iconCache[pkg] \u003d bitmap;\n cleanIconCache();\n } catch (e) {}\n }\n\n // 从缓存获取 Drawable(每次创建新的)- 优化版本\n var _BitmapDrawable \u003d null;\n var _resources \u003d null;\n\n function getCachedDrawable(pkg) {\n try {\n var bitmap \u003d iconCache[pkg];\n if (!bitmap) return null;\n\n // 缓存类引用和资源\n if (!_BitmapDrawable) {\n _BitmapDrawable \u003d Packages.android.graphics.drawable.BitmapDrawable;\n _resources \u003d ctx.getResources();\n }\n\n var drawable \u003d new _BitmapDrawable(_resources, bitmap);\n drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());\n return drawable;\n } catch (e) {\n return null;\n }\n }\n\n // 获取缓存 key\n function getCacheKey(pkg) {\n return String(pkg);\n }\n\n // 获取默认图标 - 优化版本(缓存默认图标)\n var _defaultIcon \u003d null;\n var _defaultIconColor \u003d Packages.android.graphics.Color.parseColor(\"#5C6BC0\");\n\n function getDefaultIcon() {\n if (!_defaultIcon) {\n var drawable \u003d new android.graphics.drawable.GradientDrawable();\n drawable.setShape(android.graphics.drawable.GradientDrawable.OVAL);\n drawable.setColor(_defaultIconColor);\n var size \u003d dp(ICON_DP);\n drawable.setSize(size, size);\n drawable.setBounds(0, 0, size, size);\n _defaultIcon \u003d drawable;\n }\n return _defaultIcon;\n }\n\n // 清理图标缓存 - 优化版本\n var _iconCacheKeys \u003d [];\n var _iconCacheKeyIndex \u003d 0;\n\n function cleanIconCache() {\n // 使用计数器避免频繁清理\n _iconCacheKeyIndex++;\n if (_iconCacheKeyIndex \u003c 10) return;\n _iconCacheKeyIndex \u003d 0;\n\n var count \u003d 0;\n for (var key in iconCache) {\n if (iconCache.hasOwnProperty(key)) {\n count++;\n }\n }\n if (count \u003e MAX_CACHE_SIZE) {\n // 删除最早的 20 个\n var deleted \u003d 0;\n for (var key in iconCache) {\n if (iconCache.hasOwnProperty(key)) {\n delete iconCache[key];\n deleted++;\n if (deleted \u003e\u003d 20) break;\n }\n }\n }\n }\n\n // 同步获取图标(带缓存)\n function getAppIcon(pkg, iv) {\n try {\n var cacheKey \u003d getCacheKey(pkg);\n \n // 1. 检查缓存(从 Bitmap 创建新的 Drawable)\n var cachedDrawable \u003d getCachedDrawable(pkg);\n if (cachedDrawable) {\n iv.setImageDrawable(cachedDrawable);\n return;\n }\n \n // 2. 从 PackageManager 获取\n var icon \u003d pm.getApplicationIcon(pkg);\n if (icon) {\n // 缓存为 Bitmap\n cacheDrawable(pkg, icon);\n // 使用原始图标显示\n icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());\n iv.setImageDrawable(icon);\n } else {\n iv.setImageDrawable(getDefaultIcon());\n }\n } catch (e) {\n iv.setImageDrawable(getDefaultIcon());\n }\n }\n\n // 异步加载图标(用于列表)\n function loadIconAsync(pkg, iv) {\n if (!ENABLE_LOAD_ICONS) {\n iv.setImageDrawable(getDefaultIcon());\n return;\n }\n \n \n \n // 检查缓存(从 Bitmap 创建新的 Drawable)\n var cachedDrawable \u003d getCachedDrawable(pkg);\n if (cachedDrawable) {\n iv.setImageDrawable(cachedDrawable);\n return;\n }\n \n // 后台加载\n wkHandler.post(new java.lang.Runnable({\n run: function() {\n try {\n var icon \u003d pm.getApplicationIcon(pkg);\n if (icon) {\n // 缓存为 Bitmap\n cacheDrawable(pkg, icon);\n } else {\n icon \u003d getDefaultIcon();\n }\n \n // 回到主线程更新\n uiHandler.post(new java.lang.Runnable({\n run: function() {\n if (getClosing()) return;\n // 验证 view 是否还在显示同一个应用\n var currentPkg \u003d String(iv.getTag() || \"\");\n if (currentPkg \u003d\u003d\u003d pkg) {\n // 从缓存获取(创建新的 Drawable)\n var drawable \u003d getCachedDrawable(pkg);\n if (drawable) {\n iv.setImageDrawable(drawable);\n } else {\n iv.setImageDrawable(icon);\n }\n }\n }\n }));\n } catch (e) {\n uiHandler.post(new java.lang.Runnable({\n run: function() {\n if (getClosing()) return;\n // 验证 view 是否还在显示同一个应用\n var currentPkg \u003d String(iv.getTag() || \"\");\n if (currentPkg \u003d\u003d\u003d pkg) {\n iv.setImageDrawable(getDefaultIcon());\n }\n }\n }));\n }\n }\n }));\n }\n\n // 更安全的异步加载(带位置验证)\n function loadIconAsyncSafe(pkg, targetIv) {\n var cacheKey \u003d getCacheKey(pkg);\n \n wkHandler.post(new java.lang.Runnable({\n run: function() {\n try {\n var icon \u003d pm.getApplicationIcon(pkg);\n if (!icon) icon \u003d getDefaultIcon();\n \n // 缓存为 Bitmap\n cacheDrawable(pkg, icon);\n \n uiHandler.post(new java.lang.Runnable({\n run: function() {\n if (getClosing()) return;\n \n // 验证 view 是否还在显示同一个应用\n var currentTargetPkg \u003d targetIv.getTag(TAG_HOLDER_POSITION);\n if (String(currentTargetPkg) !\u003d\u003d String(pkg)) {\n return;\n }\n \n // 从缓存获取(创建新的 Drawable)\n var drawable \u003d getCachedDrawable(pkg);\n if (drawable) {\n targetIv.setImageDrawable(drawable);\n } else {\n targetIv.setImageDrawable(icon);\n }\n }\n }));\n } catch(e) {\n // 加载失败保持默认图标\n }\n }\n }));\n }\n\n // 预加载图标列表\n function preloadIcons(pkgList) {\n if (!pkgList || pkgList.length \u003d\u003d\u003d 0) return;\n \n wkHandler.post(new java.lang.Runnable({\n run: function() {\n for (var i \u003d 0; i \u003c pkgList.length \u0026\u0026 i \u003c 10; i++) {\n var pkg \u003d pkgList[i];\n if (!iconCache[pkg]) {\n try {\n var icon \u003d pm.getApplicationIcon(pkg);\n if (icon) {\n // 缓存为 Bitmap\n cacheDrawable(pkg, icon);\n }\n } catch (e) {}\n }\n }\n cleanIconCache();\n }\n }));\n }\n\n\n var preloadSeq \u003d 0;\n\n function buildPreloadList() {\n var list \u003d [];\n var seen \u003d {};\n\n for (var i \u003d 0; i \u003c gridData.length; i++) {\n var a \u003d gridData[i];\n if (!a) continue;\n if (!isVisiblePkg(a.pkg)) continue;\n\n if (!seen[a.pkg]) {\n seen[a.pkg] \u003d 1;\n list.push(a.pkg);\n }\n if (list.length \u003e\u003d PRELOAD_COUNT) break;\n }\n return list;\n }\n\n function startPreload() {\n safe(\"startPreload\", function () {\n if (!ENABLE_LOAD_ICONS) return 1;\n if (getClosing()) return 1;\n\n preloadSeq++;\n var mySeq \u003d preloadSeq;\n\n var list \u003d buildPreloadList();\n if (!list || list.length \u003c\u003d 0) return 1;\n\n wkHandler.post(new java.lang.Runnable({\n run: function () {\n for (var i \u003d 0; i \u003c list.length; i++) {\n if (getClosing()) return;\n if (mySeq !\u003d\u003d preloadSeq) return;\n\n var pkg \u003d list[i];\n if (iconCache[pkg]) {\n SystemClock.sleep(PRELOAD_DELAY_MS);\n continue;\n }\n\n var icon \u003d safe(\"preloadIcon:\" + pkg, function () { return pm.getApplicationIcon(pkg); });\n if (icon) {\n // 缓存为 Bitmap\n cacheDrawable(pkg, icon);\n }\n\n SystemClock.sleep(PRELOAD_DELAY_MS);\n }\n }\n }));\n\n return 1;\n });\n }\n\n function preloadFirstScreenBlockingOnWK(listPkgs) {\n safe(\"preloadFirstScreenWK\", function () {\n if (!ENABLE_LOAD_ICONS) return 1;\n if (!FIRST_SCREEN_PRELOAD) return 1;\n if (!listPkgs || listPkgs.length \u003c\u003d 0) return 1;\n\n for (var i \u003d 0; i \u003c listPkgs.length; i++) {\n if (getClosing()) return 0;\n var pkg \u003d listPkgs[i];\n if (!pkg) continue;\n if (iconCache[pkg]) continue;\n\n var icon \u003d safe(\"preloadFirstIcon:\" + pkg, function () { return pm.getApplicationIcon(pkg); });\n if (icon) {\n // 缓存为 Bitmap\n cacheDrawable(pkg, icon);\n }\n }\n return 1;\n });\n }\n\n\n function enabledSettingFrozen(pkg) {\n return safe(\"enabledSettingFrozen:\" + pkg, function () {\n var st \u003d pm.getApplicationEnabledSetting(pkg);\n if (st \u003d\u003d\u003d PM.COMPONENT_ENABLED_STATE_DISABLED) return 1;\n if (st \u003d\u003d\u003d PM.COMPONENT_ENABLED_STATE_DISABLED_USER) return 1;\n if (st \u003d\u003d\u003d PM.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) return 1;\n return 0;\n }) || 0;\n }\n\n function setFrozen(pkg, frozen) {\n return safe(\"setFrozen:\" + pkg, function () {\n var ns \u003d frozen ? PM.COMPONENT_ENABLED_STATE_DISABLED_USER : PM.COMPONENT_ENABLED_STATE_ENABLED;\n pm.setApplicationEnabledSetting(pkg, ns, PM.DONT_KILL_APP);\n return 1;\n }) || 0;\n }\n\n function launchApp(pkg) {\n return safe(\"launchApp:\" + pkg, function () {\n var it \u003d pm.getLaunchIntentForPackage(pkg);\n if (!it) {\n Settings.Global.putString(\n ctx.getContentResolver(),\n KEY_LAST_LAUNCH_FAIL,\n JSON.stringify({ pkg: pkg, why: \"no_launch_intent\", ts: java.lang.System.currentTimeMillis() })\n );\n return 0;\n }\n it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n ctx.startActivity(it);\n return 1;\n }) || 0;\n }\n\n function openAppDetail(pkg) {\n safe(\"openDetail:\" + pkg, function () {\n var it \u003d new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);\n it.setData(Uri.parse(\"package:\" + pkg));\n it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n ctx.startActivity(it);\n return 1;\n });\n }\n\n function copyText(text) {\n return safe(\"copyText\", function () {\n var cm \u003d ctx.getSystemService(Packages.android.content.Context.CLIPBOARD_SERVICE);\n if (!cm) return 0;\n var cd \u003d ClipData.newPlainText(\"pkg\", String(text));\n cm.setPrimaryClip(cd);\n return 1;\n }) || 0;\n }\n\n\n var appsAll \u003d [];\n var normalList \u003d [];\n var frozenList \u003d [];\n\n var curTab \u003d loadTabFromSettings();\n var visMap \u003d loadVisMap();\n var pinned \u003d loadPinned();\n\n var gridData \u003d [];\n\n\n var appIndex \u003d {};\n\n function rebuildAppIndex() {\n appIndex \u003d {};\n for (var i \u003d 0; i \u003c appsAll.length; i++) {\n var a \u003d appsAll[i];\n if (!a || !a.pkg) continue;\n appIndex[a.pkg] \u003d a;\n }\n }\n\n function findApp(pkg) {\n return appIndex[pkg] || null;\n }\n\n function cleanPinned() {\n var out \u003d [];\n for (var i \u003d 0; i \u003c pinned.length; i++) {\n var p \u003d pinned[i];\n var a \u003d findApp(p);\n if (!a) continue;\n if (!isVisiblePkg(a.pkg)) continue;\n out.push(a.pkg);\n if (out.length \u003e\u003d PIN_STORE_MAX) break;\n }\n pinned \u003d out;\n savePinned();\n }\n\n function isPinned(pkg) {\n for (var i \u003d 0; i \u003c pinned.length; i++) {\n if (pinned[i] \u003d\u003d\u003d pkg) return true;\n }\n return false;\n }\n\n function pinTouch(pkg) {\n if (!pkg) return;\n if (!isVisiblePkg(pkg)) return;\n\n var out \u003d [pkg];\n for (var i \u003d 0; i \u003c pinned.length; i++) {\n if (pinned[i] !\u003d\u003d pkg) out.push(pinned[i]);\n if (out.length \u003e\u003d PIN_STORE_MAX) break;\n }\n pinned \u003d out;\n savePinned();\n }\n\n function unpin(pkg) {\n if (!pkg) return;\n var out \u003d [];\n for (var i \u003d 0; i \u003c pinned.length; i++) {\n if (pinned[i] !\u003d\u003d pkg) out.push(pinned[i]);\n }\n pinned \u003d out;\n savePinned();\n }\n\n function rebuildLists() {\n var n \u003d [];\n var f \u003d [];\n\n for (var i \u003d 0; i \u003c appsAll.length; i++) {\n var a \u003d appsAll[i];\n if (!a) continue;\n if (!isVisiblePkg(a.pkg)) continue;\n\n if (a.frozen) f.push(a);\n else n.push(a);\n }\n\n n.sort(function (x, y) {\n if (x.label \u003c y.label) return -1;\n if (x.label \u003e y.label) return 1;\n return 0;\n });\n\n f.sort(function (x2, y2) {\n if (x2.label \u003c y2.label) return -1;\n if (x2.label \u003e y2.label) return 1;\n return 0;\n });\n\n normalList \u003d n;\n frozenList \u003d f;\n }\n\n function chooseGridDataByTab() {\n var base \u003d (curTab \u003d\u003d\u003d TAB_FROZEN) ? frozenList : normalList;\n\n // 构建置顶集合和base中的app映射\n var pinSet \u003d {};\n var baseMap \u003d {};\n\n // 构建base映射,方便查找\n for (var k \u003d 0; k \u003c base.length; k++) {\n var a2 \u003d base[k];\n if (a2 \u0026\u0026 a2.pkg) {\n baseMap[a2.pkg] \u003d a2;\n }\n }\n\n for (var i \u003d 0; i \u003c pinned.length; i++) {\n pinSet[pinned[i]] \u003d true;\n }\n\n // 使用 seen 集合防止重复\n var seen \u003d {};\n var result \u003d [];\n\n // 首先添加置顶应用(保持置顶顺序)\n for (var j \u003d 0; j \u003c pinned.length; j++) {\n var pkg \u003d pinned[j];\n if (seen[pkg]) continue; // 跳过已添加的\n\n // 从base中查找应用,而不是appIndex\n var a \u003d baseMap[pkg];\n if (!a) continue;\n if (!isVisiblePkg(a.pkg)) continue;\n\n // 检查是否在正确的tab\n if (curTab \u003d\u003d\u003d TAB_FROZEN) {\n if (!a.frozen) continue;\n } else {\n if (a.frozen) continue;\n }\n\n seen[pkg] \u003d true;\n result.push(a);\n }\n\n // 然后添加非置顶应用\n for (var k \u003d 0; k \u003c base.length; k++) {\n var a2 \u003d base[k];\n if (!a2) continue;\n if (seen[a2.pkg]) continue; // 跳过已添加的(包括置顶的)\n if (!isVisiblePkg(a2.pkg)) continue; // 只显示可见应用\n\n seen[a2.pkg] \u003d true;\n result.push(a2);\n }\n\n gridData \u003d result;\n }\n\n function buildFirstScreenPkgList() {\n var out \u003d [];\n var seen \u003d {};\n for (var i \u003d 0; i \u003c gridData.length; i++) {\n var a \u003d gridData[i];\n if (!a) continue;\n if (!isVisiblePkg(a.pkg)) continue;\n\n if (!seen[a.pkg]) {\n seen[a.pkg] \u003d 1;\n out.push(a.pkg);\n }\n if (out.length \u003e\u003d FIRST_SCREEN_COUNT) break;\n }\n return out;\n }\n\n\n var refreshPending \u003d 0;\n var refreshRunnable \u003d null;\n\n function getRefreshPending() {\n return safe(\"getRefreshPending\", function() {\n // Rhino 不支持 synchronized 语法\n return refreshPending;\n }) || 0;\n }\n\n function setRefreshPending(val) {\n return safe(\"setRefreshPending\", function() {\n // Rhino 不支持 synchronized 语法\n refreshPending \u003d val;\n return 1;\n }) || 0;\n }\n\n function refreshTabUi() {\n safe(\"refreshTabUi\", function () {\n if (!tabNormalBtn || !tabFrozenBtn) return 1;\n\n if (curTab \u003d\u003d\u003d TAB_FROZEN) {\n // 优化Tab选中态 - 使用主色调\n tabFrozenBtn.setBackgroundDrawable(createRoundedDrawable(Colors.tabSelected, CORNER_FULL));\n tabFrozenBtn.setTextColor(Colors.tabSelectedText);\n tabNormalBtn.setBackgroundDrawable(createRoundedDrawable(Color.TRANSPARENT, CORNER_FULL));\n tabNormalBtn.setTextColor(Colors.tabUnselectedText);\n } else {\n tabNormalBtn.setBackgroundDrawable(createRoundedDrawable(Colors.tabSelected, CORNER_FULL));\n tabNormalBtn.setTextColor(Colors.tabSelectedText);\n tabFrozenBtn.setBackgroundDrawable(createRoundedDrawable(Color.TRANSPARENT, CORNER_FULL));\n tabFrozenBtn.setTextColor(Colors.tabUnselectedText);\n }\n return 1;\n });\n }\n\n function refreshDebounced() {\n safe(\"refreshDebounced\", function () {\n if (getClosing()) return 0;\n\n if (!refreshRunnable) {\n refreshRunnable \u003d new java.lang.Runnable({\n run: function () {\n safe(\"refreshRun\", function () {\n setRefreshPending(0);\n\n rebuildLists();\n chooseGridDataByTab();\n refreshTabUi();\n\n if (adapterGrid) adapterGrid.notifyDataSetChanged();\n if (adapterSettings) adapterSettings.notifyDataSetChanged();\n\n startPreload();\n return 1;\n });\n }\n });\n }\n\n if (!getRefreshPending()) setRefreshPending(1);\n\n uiHandler.removeCallbacks(refreshRunnable);\n uiHandler.postDelayed(refreshRunnable, 150);\n return 1;\n });\n }\n\n\n var ASYNC_LOAD_ENABLED \u003d true; // 启用异步加载\n var FIRST_BATCH_SIZE \u003d 20; // 首屏快速显示数量\n var PARALLEL_ICON_LOADERS \u003d 2; // 并行图标加载线程数(减少到2个避免资源浪费)\n var LABEL_LOAD_DELAY_MS \u003d 50; // 标签加载延迟\n var CHUNK_SIZE \u003d 10; // 分片加载大小\n var CHUNK_DELAY_MS \u003d 30; // 分片间隔\n\n\n var iconLoadExecutor \u003d null;\n\n function getIconExecutor() {\n if (!iconLoadExecutor) {\n // 使用 Java 原生方式创建线程池\n iconLoadExecutor \u003d java.util.concurrent.Executors.newFixedThreadPool(PARALLEL_ICON_LOADERS);\n }\n return iconLoadExecutor;\n }\n\n\n function loadIconAsyncParallel(pkg, cb) {\n if (!ENABLE_LOAD_ICONS) {\n cb(getDefaultIcon());\n return;\n }\n // 检查缓存(从 Bitmap 创建新的 Drawable)\n var cachedDrawable \u003d getCachedDrawable(pkg);\n if (cachedDrawable) {\n cb(cachedDrawable);\n return;\n }\n\n getIconExecutor().submit(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n var icon \u003d safe(\"getIconParallel:\" + pkg, function () {\n return pm.getApplicationIcon(pkg);\n });\n if (!icon) icon \u003d getDefaultIcon();\n // 缓存为 Bitmap\n cacheDrawable(pkg, icon);\n uiHandler.post(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n // 从缓存获取(创建新的 Drawable)\n var drawable \u003d getCachedDrawable(pkg);\n if (drawable) {\n cb(drawable);\n } else {\n cb(icon);\n }\n }\n }));\n }\n }));\n }\n\n\n function preloadIconsInChunks(pkgList, chunkSize, delayMs, onComplete) {\n safe(\"preloadChunks\", function () {\n if (!pkgList || pkgList.length \u003d\u003d\u003d 0) {\n if (onComplete) onComplete();\n return 1;\n }\n\n var total \u003d pkgList.length;\n var index \u003d 0;\n\n function loadNextChunk() {\n if (getClosing()) return;\n if (index \u003e\u003d total) {\n if (onComplete) onComplete();\n return;\n }\n\n var end \u003d Math.min(index + chunkSize, total);\n // 使用兼容方式获取分片(Rhino不支持slice)\n var chunk \u003d [];\n for (var sliceIdx \u003d index; sliceIdx \u003c end; sliceIdx++) {\n chunk.push(pkgList[sliceIdx]);\n }\n\n // 并行加载当前分片\n var loadedCount \u003d 0;\n var chunkSizeActual \u003d chunk.length;\n\n for (var i \u003d 0; i \u003c chunk.length; i++) {\n (function(pkg) {\n var cacheKey \u003d getCacheKey(pkg);\n if (!pkg || iconCache[cacheKey]) {\n loadedCount++;\n if (loadedCount \u003e\u003d chunkSizeActual) {\n index \u003d end;\n uiHandler.postDelayed(new java.lang.Runnable({\n run: function () { loadNextChunk(); }\n }), delayMs);\n }\n return;\n }\n\n getIconExecutor().submit(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n var icon \u003d safe(\"chunkIcon:\" + pkg, function () {\n return pm.getApplicationIcon(pkg);\n });\n if (icon) {\n // 缓存为 Bitmap\n cacheDrawable(pkg, icon);\n }\n\n loadedCount++;\n if (loadedCount \u003e\u003d chunkSizeActual) {\n index \u003d end;\n uiHandler.postDelayed(new java.lang.Runnable({\n run: function () { loadNextChunk(); }\n }), delayMs);\n }\n }\n }));\n })(chunk[i]);\n }\n }\n\n loadNextChunk();\n return 1;\n });\n }\n\n\n function scanAllUserAppsOnWK(onComplete) {\n return safe(\"scanAllUserApps\", function () {\n if (!ENABLE_SCAN_APPS) {\n if (onComplete) onComplete([]);\n return [];\n }\n\n var flags \u003d INSTALLED_MATCH_FLAGS || 0;\n var aiList \u003d pm.getInstalledApplications(flags);\n\n var out \u003d [];\n // 缓存 ApplicationInfo 常量\n var ApplicationInfo \u003d Packages.android.content.pm.ApplicationInfo;\n var FLAG_SYSTEM \u003d ApplicationInfo.FLAG_SYSTEM;\n var FLAG_UPDATED_SYSTEM_APP \u003d ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;\n\n // 处理所有用户应用(不过滤)\n for (var i \u003d 0; i \u003c aiList.size(); i++) {\n if (getClosing()) break;\n\n var ai \u003d aiList.get(i);\n var pkg \u003d String(ai.packageName);\n\n var af \u003d ai.flags;\n var isSystem \u003d (af \u0026 FLAG_SYSTEM) !\u003d\u003d 0;\n var isUpdatedSys \u003d (af \u0026 FLAG_UPDATED_SYSTEM_APP) !\u003d\u003d 0;\n var isUser \u003d (!isSystem) || isUpdatedSys;\n if (!isUser) continue;\n\n var label \u003d safe(\"loadLabel:\" + pkg, function () {\n return String(ai.loadLabel(pm));\n }) || pkg;\n\n var frozen \u003d 0;\n try {\n if (ai.enabled \u003d\u003d\u003d false) frozen \u003d 1;\n } catch (e1) {}\n\n out.push({ pkg: pkg, label: label, frozen: frozen ? 1 : 0 });\n }\n\n // 一次性返回所有应用\n if (onComplete) onComplete(out);\n\n return out;\n }) || [];\n }\n\n\n function scanVisibleAppsOnWK(onComplete) {\n return safe(\"scanVisibleApps\", function () {\n if (!ENABLE_SCAN_APPS) {\n if (onComplete) onComplete([]);\n return [];\n }\n\n var flags \u003d INSTALLED_MATCH_FLAGS || 0;\n var aiList \u003d pm.getInstalledApplications(flags);\n\n var out \u003d [];\n // 缓存 ApplicationInfo 常量\n var ApplicationInfo \u003d Packages.android.content.pm.ApplicationInfo;\n var FLAG_SYSTEM \u003d ApplicationInfo.FLAG_SYSTEM;\n var FLAG_UPDATED_SYSTEM_APP \u003d ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;\n\n // 处理所有应用\n for (var i \u003d 0; i \u003c aiList.size(); i++) {\n if (getClosing()) break;\n\n var ai \u003d aiList.get(i);\n var pkg \u003d String(ai.packageName);\n\n // 使用统一的 isVisiblePkg 函数检查可见性\n if (!isVisiblePkg(pkg)) continue;\n\n var af \u003d ai.flags;\n var isSystem \u003d (af \u0026 FLAG_SYSTEM) !\u003d\u003d 0;\n var isUpdatedSys \u003d (af \u0026 FLAG_UPDATED_SYSTEM_APP) !\u003d\u003d 0;\n var isUser \u003d (!isSystem) || isUpdatedSys;\n if (!isUser) continue;\n\n var label \u003d safe(\"loadLabel:\" + pkg, function () {\n return String(ai.loadLabel(pm));\n }) || pkg;\n\n var frozen \u003d 0;\n try {\n if (ai.enabled \u003d\u003d\u003d false) frozen \u003d 1;\n } catch (e1) {}\n\n out.push({ pkg: pkg, label: label, frozen: frozen ? 1 : 0 });\n }\n\n // 一次性返回所有应用\n if (onComplete) onComplete(out);\n\n return out;\n }) || [];\n }\n\n\n function loadLabelLazy(pkg, tempLabel, onLoaded) {\n wkHandler.post(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n\n var realLabel \u003d safe(\"loadLabelLazy:\" + pkg, function () {\n var ai \u003d pm.getApplicationInfo(pkg, 0);\n return ai ? String(ai.loadLabel(pm)) : null;\n });\n\n if (realLabel \u0026\u0026 realLabel !\u003d\u003d tempLabel) {\n uiHandler.post(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n onLoaded(realLabel);\n }\n }));\n }\n }\n }));\n }\n\n\n function startPreloadOptimized() {\n safe(\"startPreloadOptimized\", function () {\n if (!ENABLE_LOAD_ICONS) return 1;\n if (getClosing()) return 1;\n\n var list \u003d buildPreloadList();\n if (!list || list.length \u003c\u003d 0) return 1;\n\n // 使用分批并行加载\n preloadIconsInChunks(list, 6, 20, null);\n\n return 1;\n });\n }\n\n\n function preloadFirstScreenAsync(listPkgs, onComplete) {\n safe(\"preloadFirstScreenAsync\", function () {\n if (!ENABLE_LOAD_ICONS) {\n if (onComplete) onComplete();\n return 1;\n }\n if (!FIRST_SCREEN_PRELOAD) {\n if (onComplete) onComplete();\n return 1;\n }\n if (!listPkgs || listPkgs.length \u003c\u003d 0) {\n if (onComplete) onComplete();\n return 1;\n }\n\n // 并行加载首屏图标\n var loadedCount \u003d 0;\n var total \u003d listPkgs.length;\n\n for (var i \u003d 0; i \u003c listPkgs.length; i++) {\n (function(pkg) {\n if (!pkg || iconCache[pkg]) {\n loadedCount++;\n if (loadedCount \u003e\u003d total \u0026\u0026 onComplete) onComplete();\n return;\n }\n\n getIconExecutor().submit(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n var icon \u003d safe(\"firstScreenIcon:\" + pkg, function () {\n return pm.getApplicationIcon(pkg);\n });\n if (icon) {\n // 缓存为 Bitmap\n cacheDrawable(pkg, icon);\n }\n\n loadedCount++;\n if (loadedCount \u003e\u003d total \u0026\u0026 onComplete) {\n uiHandler.post(new java.lang.Runnable({\n run: function () { if (onComplete) onComplete(); }\n }));\n }\n }\n }));\n })(listPkgs[i]);\n }\n\n return 1;\n });\n }\n\n\n function scanAppsQuickOnWK() {\n return safe(\"scanAppsQuickOnWK\", function () {\n if (!ENABLE_SCAN_APPS) return [];\n\n var flags \u003d INSTALLED_MATCH_FLAGS || 0;\n var aiList \u003d pm.getInstalledApplications(flags);\n\n var out \u003d [];\n // 缓存 ApplicationInfo 常量\n var ApplicationInfo \u003d Packages.android.content.pm.ApplicationInfo;\n var FLAG_SYSTEM \u003d ApplicationInfo.FLAG_SYSTEM;\n var FLAG_UPDATED_SYSTEM_APP \u003d ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;\n\n for (var i \u003d 0; i \u003c aiList.size(); i++) {\n var ai \u003d aiList.get(i);\n\n var af \u003d ai.flags;\n var isSystem \u003d (af \u0026 FLAG_SYSTEM) !\u003d\u003d 0;\n var isUpdatedSys \u003d (af \u0026 FLAG_UPDATED_SYSTEM_APP) !\u003d\u003d 0;\n var isUser \u003d (!isSystem) || isUpdatedSys;\n if (!isUser) continue;\n\n var pkg \u003d String(ai.packageName);\n if (!pkg) continue;\n\n // 快速 label(仍然需要 loadLabel,但这是 UI 需要)\n var label \u003d safe(\"loadLabel:\" + pkg, function () { return String(ai.loadLabel(pm)); }) || pkg;\n\n // 快速冻结判定:只看 ai.enabled(不调用 getApplicationEnabledSetting)\n var frozen \u003d 0;\n try {\n if (ai.enabled \u003d\u003d\u003d false) frozen \u003d 1;\n } catch (e1) {}\n\n out.push({ pkg: pkg, label: label, frozen: frozen ? 1 : 0 });\n }\n\n return out;\n }) || [];\n }\n\n function scanAppsFullOnWK() {\n return safe(\"scanAppsFullOnWK\", function () {\n if (!ENABLE_SCAN_APPS) return [];\n\n var flags \u003d INSTALLED_MATCH_FLAGS || 0;\n var aiList \u003d pm.getInstalledApplications(flags);\n\n var out \u003d [];\n // 缓存 ApplicationInfo 常量\n var ApplicationInfo \u003d Packages.android.content.pm.ApplicationInfo;\n var FLAG_SYSTEM \u003d ApplicationInfo.FLAG_SYSTEM;\n var FLAG_UPDATED_SYSTEM_APP \u003d ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;\n\n for (var i \u003d 0; i \u003c aiList.size(); i++) {\n var ai \u003d aiList.get(i);\n\n var af \u003d ai.flags;\n var isSystem \u003d (af \u0026 FLAG_SYSTEM) !\u003d\u003d 0;\n var isUpdatedSys \u003d (af \u0026 FLAG_UPDATED_SYSTEM_APP) !\u003d\u003d 0;\n var isUser \u003d (!isSystem) || isUpdatedSys;\n if (!isUser) continue;\n\n var pkg \u003d String(ai.packageName);\n if (!pkg) continue;\n\n var label \u003d safe(\"loadLabel:\" + pkg, function () { return String(ai.loadLabel(pm)); }) || pkg;\n\n var frozen \u003d 0;\n try {\n if (ai.enabled \u003d\u003d\u003d false) frozen \u003d 1;\n } catch (e1) {}\n if (!frozen) frozen \u003d enabledSettingFrozen(pkg);\n\n out.push({ pkg: pkg, label: label, frozen: frozen ? 1 : 0 });\n }\n\n return out;\n }) || [];\n }\n\n function syncFrozenStatesOnWK() {\n safe(\"syncFrozenStatesOnWK\", function () {\n if (!FAST_STARTUP_FROZEN_SYNC) return 0;\n if (!ENABLE_SCAN_APPS) return 0;\n if (getClosing()) return 0;\n\n // 只补齐 frozen 字段,不重扫 installed list\n var changed \u003d 0;\n for (var i \u003d 0; i \u003c appsAll.length; i++) {\n if (getClosing()) return 0;\n var a \u003d appsAll[i];\n if (!a || !a.pkg) continue;\n\n var realFrozen \u003d enabledSettingFrozen(a.pkg) ? 1 : 0;\n if (a.frozen !\u003d\u003d realFrozen) {\n a.frozen \u003d realFrozen;\n changed++;\n }\n }\n\n if (changed \u003e 0) {\n uiHandler.post(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n refreshDebounced();\n }\n }));\n }\n return changed;\n });\n }\n\n function scanAppsOnWK() {\n // 默认保留全量扫描入口(用于兜底/以后扩展)\n if (FAST_STARTUP_SHOW_PANEL_EARLY || FAST_STARTUP_FROZEN_SYNC) {\n return scanAppsQuickOnWK();\n }\n return scanAppsFullOnWK();\n }\n\n\n function makeHolder2(a, b) {\n var arr \u003d ReflectArray.newInstance(ObjectClass, 2);\n ReflectArray.set(arr, 0, a);\n ReflectArray.set(arr, 1, b);\n return arr;\n }\n function holder0(h) { return ReflectArray.get(h, 0); }\n function holder1(h) { return ReflectArray.get(h, 1); }\n\n function makeHolder3(a, b, c) {\n var arr \u003d ReflectArray.newInstance(ObjectClass, 3);\n ReflectArray.set(arr, 0, a);\n ReflectArray.set(arr, 1, b);\n ReflectArray.set(arr, 2, c);\n return arr;\n }\n function holderA(h) { return ReflectArray.get(h, 0); }\n function holderB(h) { return ReflectArray.get(h, 1); }\n function holderC(h) { return ReflectArray.get(h, 2); }\n\n\n function buildCellView() {\n var wrap \u003d new LinearLayout(ctx);\n wrap.setOrientation(LinearLayout.VERTICAL);\n wrap.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);\n wrap.setPadding(dp(6), dp(10), dp(6), dp(10));\n // 更亮的卡片背景\n wrap.setBackgroundDrawable(createRoundedDrawable(Colors.cardBackground, CORNER_LARGE));\n\n var iv \u003d new ImageView(ctx);\n iv.setLayoutParams(new LinearLayout.LayoutParams(dp(ICON_DP), dp(ICON_DP)));\n // 图标自适应缩放\n iv.setScaleType(ImageView.ScaleType.FIT_CENTER);\n\n var tv \u003d new TextView(ctx);\n tv.setTextColor(Colors.onSurface);\n tv.setTextSize(Packages.android.util.TypedValue.COMPLEX_UNIT_PX, sp(11));\n tv.setLines(1);\n tv.setMaxLines(1);\n tv.setEllipsize(TextUtils.TruncateAt.END);\n tv.setGravity(Gravity.CENTER);\n tv.setMinHeight(dp(20));\n // 增加上边距\n var tvLp \u003d new LinearLayout.LayoutParams(\n LinearLayout.LayoutParams.MATCH_PARENT,\n LinearLayout.LayoutParams.WRAP_CONTENT\n );\n tvLp.topMargin \u003d dp(8);\n tv.setLayoutParams(tvLp);\n\n wrap.addView(iv);\n wrap.addView(tv);\n\n wrap.setTag(TAG_HOLDER, makeHolder2(iv, tv));\n return wrap;\n }\n\n function bindCell(cell, app) {\n safe(\"bindCell:\" + (app \u0026\u0026 app.pkg ? app.pkg : \"null\"), function () {\n if (getClosing()) return 0;\n if (!app || !app.pkg) return 0;\n\n var holder \u003d cell.getTag(TAG_HOLDER);\n if (!holder) return 0;\n\n var iv \u003d holder0(holder);\n var tv \u003d holder1(holder);\n\n // 设置标记用于追踪当前应显示的应用\n cell.setTag(TAG_PKG, app.pkg);\n iv.setTag(TAG_HOLDER_POSITION, app.pkg); // 额外标记图标视图\n\n tv.setText(app.label);\n\n // 先显示默认图标(同步)\n var defaultIcon \u003d getDefaultIcon();\n iv.setImageDrawable(defaultIcon);\n\n // 检查缓存(从 Bitmap 创建新的 Drawable)\n var cachedDrawable \u003d getCachedDrawable(app.pkg);\n if (cachedDrawable) {\n iv.setImageDrawable(cachedDrawable);\n } else {\n // 异步加载\n loadIconAsyncSafe(app.pkg, iv);\n }\n\n // Material You 风格冻结态 - 变灰显示\n if (app.frozen) {\n cell.setAlpha(0.5);\n tv.setTextColor(Colors.outline);\n cell.setBackgroundDrawable(createRoundedDrawable(Colors.cardBackgroundFrozen, CORNER_LARGE));\n } else {\n cell.setAlpha(1.0);\n tv.setTextColor(Colors.onSurface);\n cell.setBackgroundDrawable(createRoundedDrawable(Colors.cardBackground, CORNER_LARGE));\n }\n\n return 1;\n });\n }\n\n\n var opLock \u003d {};\n var lastTapTs \u003d 0;\n\n function tapTooFast() {\n var n \u003d java.lang.System.currentTimeMillis();\n if (n - lastTapTs \u003c 220) return true;\n lastTapTs \u003d n;\n return false;\n }\n\n function isLocked(pkg) { return !!opLock[pkg]; }\n function lock(pkg) { opLock[pkg] \u003d 1; }\n function unlock(pkg) { delete opLock[pkg]; }\n\n\n var root \u003d null;\n var winLp \u003d null;\n\n var tabBar \u003d null;\n var tabNormalBtn \u003d null;\n var tabFrozenBtn \u003d null;\n\n var grid \u003d null;\n var adapterGrid \u003d null;\n\n var settingsLayer \u003d null;\n var settingsBox \u003d null;\n var settingsTitle \u003d null;\n var settingsList \u003d null;\n var adapterSettings \u003d null;\n var settingsAppsAll \u003d [];\n var filteredSettingsApps \u003d []; // 搜索过滤后的应用列表\n var searchBox \u003d null;\n var searchInput \u003d null;\n var btnAllOn \u003d null;\n var btnAllOff \u003d null;\n var btnSettingsClose \u003d null;\n\n // 主面板搜索\n var mainSearchBox \u003d null;\n var mainSearchInput \u003d null;\n var filteredGridData \u003d []; // 主面板过滤后的应用列表\n\n var settingsBoxLp \u003d null;\n\n\n function hideSettings() {\n if (settingsLayer) settingsLayer.setVisibility(Packages.android.view.View.GONE);\n // 隐藏输入法\n if (searchInput) {\n var imm \u003d ctx.getSystemService(Packages.android.content.Context.INPUT_METHOD_SERVICE);\n if (imm) {\n imm.hideSoftInputFromWindow(searchInput.getWindowToken(), 0);\n }\n searchInput.setText(\"\");\n }\n // 恢复窗口焦点\n setWindowFocusable(false);\n }\n\n // 主面板搜索功能\n function setWindowFocusable(focusable) {\n safe(\"setWindowFocusable\", function () {\n if (!winLp || !wm || !root) return 0;\n if (focusable) {\n winLp.flags \u003d LayoutParams.FLAG_LAYOUT_IN_SCREEN | LayoutParams.FLAG_LAYOUT_NO_LIMITS;\n } else {\n winLp.flags \u003d LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_LAYOUT_IN_SCREEN | LayoutParams.FLAG_LAYOUT_NO_LIMITS;\n }\n wm.updateViewLayout(root, winLp);\n return 1;\n });\n }\n\n function showMainSearch() {\n safe(\"showMainSearch\", function () {\n if (!mainSearchBox) return 0;\n // 先允许焦点\n setWindowFocusable(true);\n mainSearchBox.setVisibility(Packages.android.view.View.VISIBLE);\n if (mainSearchInput) {\n mainSearchInput.setText(\"\");\n // 延迟获取焦点,等待窗口更新\n uiHandler.postDelayed(new java.lang.Runnable({\n run: function () {\n if (mainSearchInput) {\n mainSearchInput.requestFocus();\n // 显示输入法\n var imm \u003d ctx.getSystemService(Packages.android.content.Context.INPUT_METHOD_SERVICE);\n if (imm) {\n imm.showSoftInput(mainSearchInput, 0);\n }\n }\n }\n }), 100);\n }\n // 初始化过滤列表\n filteredGridData \u003d gridData.slice(0);\n if (adapterGrid) adapterGrid.notifyDataSetChanged();\n return 1;\n });\n }\n\n function hideMainSearch() {\n safe(\"hideMainSearch\", function () {\n if (!mainSearchBox) return 0;\n mainSearchBox.setVisibility(Packages.android.view.View.GONE);\n if (mainSearchInput) {\n mainSearchInput.setText(\"\");\n // 隐藏输入法\n var imm \u003d ctx.getSystemService(Packages.android.content.Context.INPUT_METHOD_SERVICE);\n if (imm) {\n imm.hideSoftInputFromWindow(mainSearchInput.getWindowToken(), 0);\n }\n }\n // 恢复禁止焦点\n setWindowFocusable(false);\n // 恢复原始列表\n filteredGridData \u003d [];\n if (adapterGrid) adapterGrid.notifyDataSetChanged();\n return 1;\n });\n }\n\n // 统一的搜索过滤函数\n function filterApps(query, sourceList, resultList, adapter) {\n if (!query || query.length \u003d\u003d\u003d 0) {\n resultList.length \u003d 0;\n for (var i \u003d 0; i \u003c sourceList.length; i++) {\n resultList.push(sourceList[i]);\n }\n } else {\n var lowerQuery \u003d String(query).toLowerCase();\n resultList.length \u003d 0;\n for (var j \u003d 0; j \u003c sourceList.length; j++) {\n var app \u003d sourceList[j];\n if (!app) continue;\n var label \u003d String(app.label || \"\").toLowerCase();\n var pkg \u003d String(app.pkg || \"\").toLowerCase();\n if (label.indexOf(lowerQuery) \u003e\u003d 0 || pkg.indexOf(lowerQuery) \u003e\u003d 0) {\n resultList.push(app);\n }\n }\n }\n if (adapter) adapter.notifyDataSetChanged();\n }\n\n function filterMainApps(query) {\n safe(\"filterMainApps\", function () {\n filterApps(query, gridData, filteredGridData, adapterGrid);\n return 1;\n });\n }\n\n function filterSettingsApps(query) {\n safe(\"filterSettingsApps\", function () {\n filterApps(query, settingsAppsAll, filteredSettingsApps, adapterSettings);\n return 1;\n });\n }\n\n\n function updateOverlaySizesFromRoot() {\n safe(\"updateOverlaySizesFromRoot\", function () {\n if (getClosing()) return false;\n if (!root) return false;\n\n var rw \u003d root.getWidth();\n var rh \u003d root.getHeight();\n\n // 延迟重试如果尺寸未就绪\n if (rw \u003c\u003d 0 || rh \u003c\u003d 0) {\n uiHandler.postDelayed(new java.lang.Runnable({\n run: function () {\n updateOverlaySizesFromRoot();\n }\n }), 100);\n return false;\n }\n\n if (settingsBox \u0026\u0026 settingsBoxLp) {\n var sw \u003d rw - dp(30);\n sw \u003d clamp(sw, dp(300), dp(760));\n\n var sh \u003d Math.floor(rh * 0.85);\n sh \u003d clamp(sh, dp(260), rh - dp(40));\n\n settingsBoxLp.width \u003d sw;\n settingsBoxLp.height \u003d sh;\n settingsBox.setLayoutParams(settingsBoxLp);\n }\n\n return true;\n });\n }\n\n\n function actToggleFreeze(pkg) {\n if (!pkg) return;\n if (tapTooFast()) return;\n if (isLocked(pkg)) return;\n\n lock(pkg);\n\n wkHandler.post(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n\n var frozen \u003d enabledSettingFrozen(pkg);\n setFrozen(pkg, frozen ? 0 : 1);\n SystemClock.sleep(120);\n\n uiHandler.post(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n\n unlock(pkg);\n\n\n // 只更新单个 app 状态,避免全量 scan\n var a \u003d findApp(pkg);\n if (a) {\n a.frozen \u003d frozen ? 0 : 1;\n refreshDebounced();\n } else {\n refreshAfterActionFull();\n }\n }\n }));\n }\n }));\n }\n\n function refreshAfterActionFull() {\n wkHandler.post(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n\n var list \u003d scanAppsOnWK();\n\n uiHandler.post(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n\n appsAll \u003d list;\n rebuildAppIndex();\n cleanPinned();\n\n refreshDebounced();\n }\n }));\n }\n }));\n }\n function makeSmallBtn(text) {\n var tv \u003d new TextView(ctx);\n tv.setText(String(text));\n tv.setTextColor(Colors.onSecondaryContainer);\n tv.setTextSize(14);\n tv.setGravity(Gravity.CENTER);\n tv.setPadding(dp(14), dp(10), dp(14), dp(10));\n // Material You 次色调容器按钮\n tv.setBackgroundDrawable(createRoundedDrawable(Colors.secondaryContainer, CORNER_FULL));\n return tv;\n }\n\n function showSettings() {\n safe(\"showSettings\", function () {\n if (getClosing()) return 0;\n if (!settingsLayer) return 0;\n\n updateOverlaySizesFromRoot();\n\n // 允许焦点(用于显示输入法)\n setWindowFocusable(true);\n\n // 加载所有用户应用到设置列表\n wkHandler.post(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n scanAllUserAppsOnWK(function (allApps) {\n uiHandler.post(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n // 临时替换appsAll用于设置列表显示\n settingsAppsAll \u003d allApps;\n // 初始化过滤列表为所有应用\n filteredSettingsApps \u003d allApps.slice(0);\n // 清空搜索框\n if (searchInput) searchInput.setText(\"\");\n settingsLayer.setVisibility(Packages.android.view.View.VISIBLE);\n if (adapterSettings) adapterSettings.notifyDataSetChanged();\n }\n }));\n\n // 后台预加载设置面板中的图标\n preloadSettingsIcons(allApps);\n });\n }\n }));\n return 1;\n });\n }\n\n // 预加载设置面板中的图标\n function preloadSettingsIcons(apps) {\n if (!apps || apps.length \u003d\u003d\u003d 0) return;\n\n wkHandler.post(new java.lang.Runnable({\n run: function() {\n // 只预加载前30个应用的图标,避免阻塞\n var preloadCount \u003d Math.min(apps.length, 30);\n for (var i \u003d 0; i \u003c preloadCount; i++) {\n if (getClosing()) return;\n var app \u003d apps[i];\n if (!app || !app.pkg) continue;\n\n // 检查是否已缓存\n if (iconCache[app.pkg]) continue;\n\n try {\n var icon \u003d pm.getApplicationIcon(app.pkg);\n if (icon) {\n cacheDrawable(app.pkg, icon);\n }\n } catch (e) {}\n }\n\n // 刷新设置列表显示\n uiHandler.post(new java.lang.Runnable({\n run: function() {\n if (getClosing()) return;\n if (adapterSettings) adapterSettings.notifyDataSetChanged();\n }\n }));\n }\n }));\n }\n\n function buildSettingsRowView() {\n var row \u003d new LinearLayout(ctx);\n row.setOrientation(LinearLayout.HORIZONTAL);\n row.setGravity(Gravity.CENTER_VERTICAL);\n row.setPadding(dp(12), dp(12), dp(12), dp(12));\n\n var iv \u003d new ImageView(ctx);\n var ivLp \u003d new LinearLayout.LayoutParams(dp(40), dp(40));\n iv.setLayoutParams(ivLp);\n row.addView(iv);\n\n var tv \u003d new TextView(ctx);\n tv.setTextColor(Colors.onSurface);\n tv.setTextSize(14);\n tv.setLines(2);\n tv.setMaxLines(2);\n tv.setEllipsize(TextUtils.TruncateAt.END);\n\n var tvLp \u003d new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT);\n tvLp.leftMargin \u003d dp(16);\n tvLp.weight \u003d 1;\n tv.setLayoutParams(tvLp);\n row.addView(tv);\n\n var cb \u003d new CheckBox(ctx);\n cb.setText(\"\");\n cb.setTextColor(Colors.primary);\n row.addView(cb);\n\n row.setTag(TAG_ROW_HOLDER, makeHolder3(iv, tv, cb));\n return row;\n }\n\n function bindSettingsRow(row, app) {\n safe(\"bindSettingsRow:\" + (app \u0026\u0026 app.pkg ? app.pkg : \"null\"), function () {\n if (!app || !app.pkg) return 0;\n\n var h \u003d row.getTag(TAG_ROW_HOLDER);\n if (!h) return 0;\n\n var iv \u003d holderA(h);\n var tv \u003d holderB(h);\n var cb \u003d holderC(h);\n\n row.setTag(TAG_ROW_PKG, app.pkg);\n\n var suffix \u003d app.frozen ? \" [冻结]\" : \"\";\n tv.setText(app.label + suffix + \"\\n\" + app.pkg);\n\n // 先显示默认图标\n iv.setImageDrawable(getDefaultIcon());\n\n // 设置tag用于异步加载后验证\n iv.setTag(app.pkg);\n\n // 尝试从缓存获取图标\n var cachedDrawable \u003d getCachedDrawable(app.pkg);\n if (cachedDrawable) {\n iv.setImageDrawable(cachedDrawable);\n } else {\n // 异步加载真实图标\n loadIconAsync(app.pkg, iv);\n }\n\n cb.setOnCheckedChangeListener(null);\n cb.setChecked(isVisiblePkg(app.pkg) ? true : false);\n\n cb.setOnCheckedChangeListener(new Packages.android.widget.CompoundButton.OnCheckedChangeListener({\n onCheckedChanged: function (buttonView, isChecked) {\n safe(\"visToggle\", function () {\n if (getClosing()) return 0;\n\n var pkg \u003d String(row.getTag(TAG_ROW_PKG));\n if (!pkg) return 0;\n\n setVisiblePkg(pkg, isChecked ? 1 : 0);\n cleanPinned();\n\n refreshDebounced();\n return 1;\n });\n }\n }));\n\n return 1;\n });\n }\n\n function applyAllVisible(val) {\n safe(\"applyAllVisible\", function () {\n // 应用到所有用户应用(使用 settingsAppsAll)\n var targetApps \u003d settingsAppsAll.length \u003e 0 ? settingsAppsAll : appsAll;\n for (var i \u003d 0; i \u003c targetApps.length; i++) {\n var app \u003d targetApps[i];\n if (!app || !app.pkg) continue;\n visMap[app.pkg] \u003d val ? 1 : 0;\n }\n saveVisMap();\n\n cleanPinned();\n refreshDebounced();\n // 重新应用当前搜索过滤\n if (searchInput) {\n var query \u003d String(searchInput.getText() || \"\");\n filterSettingsApps(query);\n }\n return 1;\n });\n }\n\n\n function tabBtn(text) {\n var tv \u003d new TextView(ctx);\n tv.setText(String(text));\n tv.setTextColor(Colors.onSurface);\n tv.setTextSize(14);\n tv.setGravity(Gravity.CENTER);\n tv.setPadding(dp(16), dp(10), dp(16), dp(10));\n return tv;\n }\n\n function switchTab(newTab) {\n uiHandler.post(new java.lang.Runnable({\n run: function () {\n safe(\"switchTab\", function () {\n if (getClosing()) return 0;\n\n if (newTab !\u003d\u003d TAB_FROZEN) newTab \u003d TAB_NORMAL;\n if (curTab \u003d\u003d\u003d newTab) return 1;\n\n curTab \u003d newTab;\n saveTabToSettings(curTab);\n\n\n hideSettings();\n\n refreshDebounced();\n return 1;\n });\n }\n }));\n }\n\n\n function buildTabBar() {\n tabBar \u003d new LinearLayout(ctx);\n tabBar.setOrientation(LinearLayout.HORIZONTAL);\n tabBar.setPadding(dp(4), dp(4), dp(4), dp(4));\n tabBar.setBackgroundDrawable(createRoundedDrawable(Colors.tabBackground, CORNER_FULL));\n\n tabNormalBtn \u003d tabBtn(\"正常\");\n tabFrozenBtn \u003d tabBtn(\"冻结\");\n\n var lp1 \u003d new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT);\n lp1.weight \u003d 1;\n var lp2 \u003d new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT);\n lp2.weight \u003d 1;\n\n tabNormalBtn.setLayoutParams(lp1);\n tabFrozenBtn.setLayoutParams(lp2);\n\n tabNormalBtn.setOnClickListener(new Packages.android.view.View.OnClickListener({\n onClick: function () { switchTab(TAB_NORMAL); }\n }));\n tabFrozenBtn.setOnClickListener(new Packages.android.view.View.OnClickListener({\n onClick: function () { switchTab(TAB_FROZEN); }\n }));\n\n tabBar.addView(tabNormalBtn);\n tabBar.addView(tabFrozenBtn);\n\n refreshTabUi();\n return tabBar;\n }\n\n\n function closePanel() {\n uiHandler.post(new java.lang.Runnable({\n run: function () {\n safe(\"closePanel\", function () {\n var now \u003d java.lang.System.currentTimeMillis();\n if (now - lastCloseTapTs \u003c 300) return 1;\n lastCloseTapTs \u003d now;\n\n if (getClosing()) return 1;\n setClosing(1);\n\n safe(\"rmUIQueue\", function () { uiHandler.removeCallbacksAndMessages(null); return 1; });\n safe(\"rmWKQueue\", function () { wkHandler.removeCallbacksAndMessages(null); return 1; });\n\n safe(\"removeImmediate\", function () {\n if (root) {\n wm.removeViewImmediate(root);\n root \u003d null;\n }\n return 1;\n });\n\n safe(\"quitWK\", function () { wkHt.quitSafely(); return 1; });\n safe(\"quitUI\", function () { uiHt.quitSafely(); return 1; });\n\n // 关闭线程池,防止内存泄漏\n safe(\"shutdownExecutor\", function () {\n if (iconLoadExecutor) {\n iconLoadExecutor.shutdown();\n iconLoadExecutor \u003d null;\n }\n return 1;\n });\n\n return 1;\n });\n }\n }));\n }\n\n\n function installTitleDrag(titleView) {\n var downRawX \u003d 0;\n var downRawY \u003d 0;\n var downWinX \u003d 0;\n var downWinY \u003d 0;\n\n var moved \u003d 0;\n var dragging \u003d 0;\n\n var downTs \u003d 0;\n var longPressRunnable \u003d null;\n\n function cancelLongPress() {\n if (longPressRunnable) {\n uiHandler.removeCallbacks(longPressRunnable);\n longPressRunnable \u003d null;\n }\n }\n\n function clampAndApply(x, y, saveLater) {\n safe(\"clampAndApply\", function () {\n if (getClosing()) return 0;\n if (!winLp) return 0;\n\n var w \u003d dp(WIN_W_DP);\n var h \u003d dp(WIN_H_DP);\n\n var maxX \u003d Math.max(0, screenW() - w);\n var maxY \u003d Math.max(0, screenH() - h);\n\n var nx \u003d clamp(x, 0, maxX);\n var ny \u003d clamp(y, 0, maxY);\n\n winLp.x \u003d nx;\n winLp.y \u003d ny;\n\n wm.updateViewLayout(root, winLp);\n\n if (saveLater) savePosToSettings(nx, ny);\n return 1;\n });\n }\n\n titleView.setOnTouchListener(new Packages.android.view.View.OnTouchListener({\n onTouch: function (v, ev) {\n return safe(\"titleDragTouch\", function () {\n if (getClosing()) return true;\n if (settingsLayer \u0026\u0026 settingsLayer.getVisibility() \u003d\u003d\u003d Packages.android.view.View.VISIBLE) return false;\n\n var act \u003d ev.getActionMasked();\n\n if (act \u003d\u003d\u003d MotionEvent.ACTION_DOWN) {\n moved \u003d 0;\n dragging \u003d 0;\n downTs \u003d java.lang.System.currentTimeMillis();\n\n downRawX \u003d ev.getRawX();\n downRawY \u003d ev.getRawY();\n\n downWinX \u003d winLp ? winLp.x : 0;\n downWinY \u003d winLp ? winLp.y : 0;\n\n cancelLongPress();\n\n longPressRunnable \u003d new java.lang.Runnable({\n run: function () {\n safe(\"titleLongPressRun\", function () {\n if (getClosing()) return 0;\n var holdMs \u003d java.lang.System.currentTimeMillis() - downTs;\n if (holdMs \u003c DRAG_LONGPRESS_MS) return 1;\n if (moved) return 1;\n dragging \u003d 1;\n return 1;\n });\n }\n });\n\n uiHandler.postDelayed(longPressRunnable, DRAG_LONGPRESS_MS);\n return true;\n }\n\n if (act \u003d\u003d\u003d MotionEvent.ACTION_MOVE) {\n var dx \u003d ev.getRawX() - downRawX;\n var dy \u003d ev.getRawY() - downRawY;\n\n if (Math.abs(dx) \u003e dp(DRAG_SLOP_DP) || Math.abs(dy) \u003e dp(DRAG_SLOP_DP)) moved \u003d 1;\n\n if (dragging) {\n cancelLongPress();\n clampAndApply(downWinX + Math.floor(dx), downWinY + Math.floor(dy), false);\n return true;\n }\n return true;\n }\n\n if (act \u003d\u003d\u003d MotionEvent.ACTION_UP || act \u003d\u003d\u003d MotionEvent.ACTION_CANCEL) {\n cancelLongPress();\n if (dragging) {\n dragging \u003d 0;\n safe(\"savePosOnUp\", function () {\n if (getClosing()) return 0;\n if (!winLp) return 0;\n savePosToSettings(winLp.x, winLp.y);\n return 1;\n });\n return true;\n }\n return true;\n }\n\n return true;\n }) \u003d\u003d\u003d true;\n }\n }));\n }\n\n\n function buildTitleBar() {\n var bar \u003d new LinearLayout(ctx);\n bar.setOrientation(LinearLayout.HORIZONTAL);\n bar.setPadding(dp(4), dp(4), dp(4), dp(4));\n bar.setGravity(Gravity.CENTER_VERTICAL);\n\n var title \u003d new TextView(ctx);\n title.setText(\"应用\");\n title.setTextColor(Colors.onSurface);\n title.setTextSize(20);\n title.setTypeface(null, android.graphics.Typeface.BOLD);\n title.setEllipsize(TextUtils.TruncateAt.END);\n title.setSingleLine(true);\n\n var titleLp \u003d new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT);\n titleLp.weight \u003d 1;\n title.setLayoutParams(titleLp);\n\n // Material You 风格设置按钮 - 圆形背景\n var setBtn \u003d new TextView(ctx);\n setBtn.setText(\"⚙\");\n setBtn.setTextColor(Colors.onSurfaceVariant);\n setBtn.setTextSize(18);\n setBtn.setPadding(dp(12), dp(8), dp(12), dp(8));\n setBtn.setBackgroundDrawable(createRoundedDrawable(Colors.surfaceVariant, CORNER_FULL));\n setBtn.setTextColor(Colors.onSurfaceVariant);\n setBtn.setOnClickListener(new Packages.android.view.View.OnClickListener({\n onClick: function () { showSettings(); }\n }));\n\n // Material You 风格关闭按钮 - 圆形背景\n var closeBtn \u003d new TextView(ctx);\n closeBtn.setText(\"✕\");\n closeBtn.setTextColor(Colors.onSurfaceVariant);\n closeBtn.setTextSize(18);\n closeBtn.setPadding(dp(12), dp(8), dp(12), dp(8));\n closeBtn.setBackgroundDrawable(createRoundedDrawable(Colors.surfaceVariant, CORNER_FULL));\n closeBtn.setTextColor(Colors.onSurfaceVariant);\n closeBtn.setOnClickListener(new Packages.android.view.View.OnClickListener({\n onClick: function () { closePanel(); }\n }));\n\n bar.addView(title);\n\n // 添加按钮间距\n var btnSpaceLp \u003d new LinearLayout.LayoutParams(dp(8), dp(1));\n var btnSpace \u003d new TextView(ctx);\n btnSpace.setLayoutParams(btnSpaceLp);\n bar.addView(btnSpace);\n\n // 搜索按钮(主面板)\n var mainSearchBtn \u003d new TextView(ctx);\n mainSearchBtn.setText(\"🔍\");\n mainSearchBtn.setTextSize(18);\n mainSearchBtn.setPadding(dp(12), dp(8), dp(12), dp(8));\n mainSearchBtn.setBackgroundDrawable(createRoundedDrawable(Colors.surfaceVariant, CORNER_FULL));\n mainSearchBtn.setTextColor(Colors.onSurfaceVariant);\n mainSearchBtn.setOnClickListener(new Packages.android.view.View.OnClickListener({\n onClick: function () {\n safe(\"mainSearchClick\", function () {\n // 切换搜索框显示/隐藏\n if (mainSearchBox \u0026\u0026 mainSearchBox.getVisibility() \u003d\u003d\u003d Packages.android.view.View.VISIBLE) {\n hideMainSearch();\n } else {\n showMainSearch();\n }\n return 1;\n });\n }\n }));\n bar.addView(mainSearchBtn);\n\n // 添加按钮间距\n var btnSpace1 \u003d new TextView(ctx);\n btnSpace1.setLayoutParams(btnSpaceLp);\n bar.addView(btnSpace1);\n\n bar.addView(setBtn);\n\n // 添加按钮间距\n var btnSpace2 \u003d new TextView(ctx);\n btnSpace2.setLayoutParams(btnSpaceLp);\n bar.addView(btnSpace2);\n\n bar.addView(closeBtn);\n\n installTitleDrag(title);\n return bar;\n }\n\n\n function buildAndShowPanelOnUI() {\n safe(\"buildAndShowPanelOnUI\", function () {\n if (root) return 1;\n\n root \u003d new FrameLayout(ctx);\n\n // Material You 风格背景 - 深色玻璃拟态\n var mainBg \u003d createRoundedDrawable(Colors.background, CORNER_XLARGE);\n root.setBackgroundDrawable(mainBg);\n root.setAlpha(0.98);\n\n // 添加轻微阴影效果\n root.setElevation(dp(8));\n\n var container \u003d new LinearLayout(ctx);\n container.setOrientation(LinearLayout.VERTICAL);\n container.setPadding(dp(16), dp(16), dp(16), dp(16));\n\n container.addView(buildTitleBar(), new LinearLayout.LayoutParams(\n LinearLayout.LayoutParams.MATCH_PARENT,\n LinearLayout.LayoutParams.WRAP_CONTENT\n ));\n\n // 主面板搜索框(默认隐藏)\n mainSearchBox \u003d new LinearLayout(ctx);\n mainSearchBox.setOrientation(LinearLayout.HORIZONTAL);\n mainSearchBox.setPadding(dp(8), dp(8), dp(8), dp(8));\n mainSearchBox.setVisibility(Packages.android.view.View.GONE);\n\n mainSearchInput \u003d new Packages.android.widget.EditText(ctx);\n mainSearchInput.setHint(\"搜索应用...\");\n mainSearchInput.setTextColor(Colors.onSurface);\n mainSearchInput.setHintTextColor(Colors.outline);\n mainSearchInput.setBackgroundDrawable(createRoundedDrawable(Colors.surfaceVariant, CORNER_MEDIUM));\n mainSearchInput.setPadding(dp(12), dp(10), dp(12), dp(10));\n var mainSearchInputLp \u003d new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT);\n mainSearchInputLp.weight \u003d 1;\n mainSearchInput.setLayoutParams(mainSearchInputLp);\n\n // 监听文本变化(带防抖)\n var mainSearchDebounceRunnable \u003d null;\n mainSearchInput.addTextChangedListener(new Packages.android.text.TextWatcher({\n beforeTextChanged: function (s, start, count, after) {},\n onTextChanged: function (s, start, before, count) {\n safe(\"mainSearchTextChanged\", function () {\n // 取消之前的延迟任务\n if (mainSearchDebounceRunnable) {\n uiHandler.removeCallbacks(mainSearchDebounceRunnable);\n }\n // 创建新的延迟任务\n var query \u003d String(s || \"\").toLowerCase();\n mainSearchDebounceRunnable \u003d new java.lang.Runnable({\n run: function () {\n filterMainApps(query);\n }\n });\n uiHandler.postDelayed(mainSearchDebounceRunnable, 100); // 100ms防抖\n return 1;\n });\n },\n afterTextChanged: function (s) {}\n }));\n\n mainSearchBox.addView(mainSearchInput);\n\n // 清除按钮\n var mainClearBtn \u003d new TextView(ctx);\n mainClearBtn.setText(\"✕\");\n mainClearBtn.setTextSize(18);\n mainClearBtn.setTextColor(Colors.onSurfaceVariant);\n mainClearBtn.setPadding(dp(12), dp(8), dp(12), dp(8));\n mainClearBtn.setOnClickListener(new Packages.android.view.View.OnClickListener({\n onClick: function () {\n safe(\"mainClearSearch\", function () {\n mainSearchInput.setText(\"\");\n filterMainApps(\"\");\n return 1;\n });\n }\n }));\n mainSearchBox.addView(mainClearBtn);\n\n container.addView(mainSearchBox);\n\n var tabLp \u003d new LinearLayout.LayoutParams(\n LinearLayout.LayoutParams.MATCH_PARENT,\n LinearLayout.LayoutParams.WRAP_CONTENT\n );\n tabLp.topMargin \u003d dp(6);\n container.addView(buildTabBar(), tabLp);\n\n grid \u003d new GridView(ctx);\n grid.setNumColumns(COLS);\n grid.setVerticalSpacing(dp(12));\n grid.setHorizontalSpacing(dp(12));\n grid.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);\n grid.setPadding(dp(4), dp(12), dp(4), dp(8));\n grid.setClickable(true);\n grid.setFocusable(true);\n grid.setFocusableInTouchMode(true);\n\n adapterGrid \u003d new JavaAdapter(BaseAdapter, {\n getCount: function () {\n try {\n // 如果有搜索结果,使用过滤列表,否则使用原列表\n return filteredGridData.length \u003e 0 ? filteredGridData.length : gridData.length;\n } catch (e) { return 0; }\n },\n getItem: function (pos) {\n try {\n return filteredGridData.length \u003e 0 ? filteredGridData[pos] : gridData[pos];\n } catch (e) { return null; }\n },\n getItemId: function (pos) { return pos; },\n getView: function (pos, convertView, parent) {\n try {\n // 根据是否有搜索结果选择数据源\n var app \u003d filteredGridData.length \u003e 0 ? filteredGridData[pos] : gridData[pos];\n var cell \u003d convertView;\n if (cell \u003d\u003d null || cell.getTag(TAG_HOLDER) \u003d\u003d null) cell \u003d buildCellView();\n if (app) bindCell(cell, app);\n return cell;\n } catch (e) {\n var fb \u003d convertView;\n if (fb \u003d\u003d null || (fb.getTag \u0026\u0026 fb.getTag(TAG_HOLDER) \u003d\u003d null)) fb \u003d buildCellView();\n return fb;\n }\n }\n });\n\n grid.setAdapter(adapterGrid);\n\n // 设置点击事件 - 修复版\n grid.setOnItemClickListener(new Packages.android.widget.AdapterView.OnItemClickListener({\n onItemClick: function(parent, view, position, id) {\n safe(\"gridClick\", function() {\n if (getClosing()) return 0;\n\n // 优先从 view tag 获取 pkg(最可靠,避免数据源变化导致错位)\n var pkg \u003d view.getTag(TAG_PKG);\n\n // 如果 tag 没有,从数据源获取(兼容逻辑)\n if (!pkg) {\n var dataSource \u003d filteredGridData.length \u003e 0 ? filteredGridData : gridData;\n if (position \u003c 0 || position \u003e\u003d dataSource.length) return 0;\n var app \u003d dataSource[position];\n if (!app || !app.pkg) return 0;\n pkg \u003d app.pkg;\n }\n\n // 强制转换为字符串\n pkg \u003d String(pkg);\n\n // 获取应用信息(从数据源或Tag)\n var appInfo \u003d null;\n var dataSource \u003d filteredGridData.length \u003e 0 ? filteredGridData : gridData;\n if (position \u003e\u003d 0 \u0026\u0026 position \u003c dataSource.length) {\n appInfo \u003d dataSource[position];\n }\n\n // 检查应用是否被冻结\n if (appInfo \u0026\u0026 appInfo.frozen) {\n // 先解冻,然后延迟启动\n actToggleFreeze(pkg);\n\n // 延迟后启动(等待解冻完成)\n uiHandler.postDelayed(new java.lang.Runnable({\n run: function() {\n try {\n var it \u003d pm.getLaunchIntentForPackage(pkg);\n if (it) {\n it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n ctx.startActivity(it);\n closePanel();\n }\n } catch(e) {}\n }\n }), 200);\n return 1;\n }\n\n // 正常启动(未冻结状态)\n var it \u003d pm.getLaunchIntentForPackage(pkg);\n if (it) {\n it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n ctx.startActivity(it);\n closePanel();\n } else {\n // 显示错误提示\n try {\n var Toast \u003d Packages.android.widget.Toast;\n Toast.makeText(ctx, \"无法启动应用: \" + pkg, Toast.LENGTH_SHORT).show();\n } catch(e) {}\n }\n return 1;\n });\n }\n }));\n\n // 长按:置顶/取消置顶\n grid.setOnItemLongClickListener(new Packages.android.widget.AdapterView.OnItemLongClickListener({\n onItemLongClick: function(parent, view, position, id) {\n return safe(\"gridLongClick\", function() {\n if (getClosing()) return false;\n\n var pkg \u003d view.getTag(TAG_PKG);\n if (!pkg) {\n var dataSource \u003d filteredGridData.length \u003e 0 ? filteredGridData : gridData;\n if (position \u003c 0 || position \u003e\u003d dataSource.length) return false;\n var app \u003d dataSource[position];\n if (!app || !app.pkg) return false;\n pkg \u003d app.pkg;\n }\n\n // 强制转换为字符串\n pkg \u003d String(pkg);\n\n // 置顶/取消置顶切换\n var isCurrentlyPinned \u003d isPinned(pkg);\n if (isCurrentlyPinned) {\n unpin(pkg);\n try {\n var Toast \u003d Packages.android.widget.Toast;\n Toast.makeText(ctx, \"已取消置顶\", Toast.LENGTH_SHORT).show();\n } catch(e) {}\n } else {\n pinTouch(pkg);\n try {\n var Toast \u003d Packages.android.widget.Toast;\n Toast.makeText(ctx, \"已置顶\", Toast.LENGTH_SHORT).show();\n } catch(e) {}\n }\n\n // 清空搜索过滤,强制使用 gridData 避免重复\n filteredGridData \u003d [];\n\n // 刷新列表显示\n chooseGridDataByTab();\n if (adapterGrid) adapterGrid.notifyDataSetChanged();\n\n // 预加载首屏图标\n if (ENABLE_LOAD_ICONS) {\n var firstPkgs \u003d buildFirstScreenPkgList();\n preloadIcons(firstPkgs);\n }\n\n return true;\n }) || false;\n }\n }));\n\n var gridLp \u003d new LinearLayout.LayoutParams(\n LinearLayout.LayoutParams.MATCH_PARENT,\n 0\n );\n gridLp.weight \u003d 1;\n container.addView(grid, gridLp);\n\n root.addView(container);\n\n settingsLayer \u003d new FrameLayout(ctx);\n settingsLayer.setBackgroundDrawable(new ColorDrawable(Colors.scrim));\n settingsLayer.setVisibility(Packages.android.view.View.GONE);\n settingsLayer.setClickable(true);\n settingsLayer.setOnClickListener(new Packages.android.view.View.OnClickListener({\n onClick: function () { hideSettings(); }\n }));\n\n settingsBox \u003d new LinearLayout(ctx);\n settingsBox.setOrientation(LinearLayout.VERTICAL);\n settingsBox.setPadding(dp(24), dp(24), dp(24), dp(24));\n // Material You 风格背景\n settingsBox.setBackgroundDrawable(createRoundedDrawable(Colors.surface, CORNER_XLARGE));\n settingsBox.setClickable(true);\n\n var sW \u003d dp(WIN_W_DP) - dp(30);\n sW \u003d clamp(sW, dp(300), dp(760));\n var sH \u003d dp(440);\n\n settingsBoxLp \u003d new FrameLayout.LayoutParams(sW, sH);\n settingsBoxLp.gravity \u003d Gravity.CENTER;\n\n settingsTitle \u003d new TextView(ctx);\n settingsTitle.setText(\"显示设置\");\n settingsTitle.setTextColor(Colors.onSurface);\n settingsTitle.setTextSize(20);\n settingsTitle.setTypeface(null, android.graphics.Typeface.BOLD);\n\n // 标题栏容器(包含标题和关闭按钮)\n var titleBarContainer \u003d new LinearLayout(ctx);\n titleBarContainer.setOrientation(LinearLayout.HORIZONTAL);\n titleBarContainer.setGravity(Gravity.CENTER_VERTICAL);\n\n var titleLp \u003d new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT);\n titleLp.weight \u003d 1;\n settingsTitle.setLayoutParams(titleLp);\n titleBarContainer.addView(settingsTitle);\n\n // 关闭按钮\n var closeSettingsBtn \u003d new TextView(ctx);\n closeSettingsBtn.setText(\"✕\");\n closeSettingsBtn.setTextSize(20);\n closeSettingsBtn.setPadding(dp(12), dp(4), dp(12), dp(4));\n closeSettingsBtn.setBackgroundDrawable(createRoundedDrawable(Colors.surfaceVariant, CORNER_FULL));\n closeSettingsBtn.setTextColor(Colors.onSurfaceVariant);\n closeSettingsBtn.setOnClickListener(new Packages.android.view.View.OnClickListener({\n onClick: function () { hideSettings(); }\n }));\n titleBarContainer.addView(closeSettingsBtn);\n\n settingsBox.addView(titleBarContainer);\n\n // 搜索框(默认显示,无需点击按钮)\n searchBox \u003d new LinearLayout(ctx);\n searchBox.setOrientation(LinearLayout.HORIZONTAL);\n searchBox.setPadding(0, dp(8), 0, dp(12));\n\n searchInput \u003d new Packages.android.widget.EditText(ctx);\n searchInput.setHint(\"搜索应用...\");\n searchInput.setTextColor(Colors.onSurface);\n searchInput.setHintTextColor(Colors.outline);\n searchInput.setBackgroundDrawable(createRoundedDrawable(Colors.surfaceVariant, CORNER_MEDIUM));\n searchInput.setPadding(dp(12), dp(10), dp(12), dp(10));\n var searchInputLp \u003d new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT);\n searchInputLp.weight \u003d 1;\n searchInput.setLayoutParams(searchInputLp);\n\n // 监听文本变化(带防抖)\n var settingsSearchDebounceRunnable \u003d null;\n searchInput.addTextChangedListener(new Packages.android.text.TextWatcher({\n beforeTextChanged: function (s, start, count, after) {},\n onTextChanged: function (s, start, before, count) {\n safe(\"searchTextChanged\", function () {\n // 取消之前的延迟任务\n if (settingsSearchDebounceRunnable) {\n uiHandler.removeCallbacks(settingsSearchDebounceRunnable);\n }\n // 创建新的延迟任务\n var query \u003d String(s || \"\").toLowerCase();\n settingsSearchDebounceRunnable \u003d new java.lang.Runnable({\n run: function () {\n filterSettingsApps(query);\n }\n });\n uiHandler.postDelayed(settingsSearchDebounceRunnable, 100); // 100ms防抖\n return 1;\n });\n },\n afterTextChanged: function (s) {}\n }));\n\n searchBox.addView(searchInput);\n\n // 清除按钮\n var clearBtn \u003d new TextView(ctx);\n clearBtn.setText(\"✕\");\n clearBtn.setTextSize(18);\n clearBtn.setTextColor(Colors.onSurfaceVariant);\n clearBtn.setPadding(dp(12), dp(8), dp(12), dp(8));\n clearBtn.setOnClickListener(new Packages.android.view.View.OnClickListener({\n onClick: function () {\n safe(\"clearSearch\", function () {\n searchInput.setText(\"\");\n filterSettingsApps(\"\");\n return 1;\n });\n }\n }));\n searchBox.addView(clearBtn);\n\n settingsBox.addView(searchBox);\n\n // 应用列表\n settingsList \u003d new ListView(ctx);\n settingsList.setDivider(new ColorDrawable(Colors.outlineVariant));\n settingsList.setDividerHeight(dp(1));\n settingsList.setCacheColorHint(Color.TRANSPARENT);\n\n adapterSettings \u003d new JavaAdapter(BaseAdapter, {\n getCount: function () { try { return filteredSettingsApps.length; } catch (e) { return 0; } },\n getItem: function (pos) { try { return filteredSettingsApps[pos]; } catch (e) { return null; } },\n getItemId: function (pos) { return pos; },\n getView: function (pos, convertView, parent) {\n try {\n var app \u003d filteredSettingsApps[pos];\n var row \u003d convertView;\n if (row \u003d\u003d null || row.getTag(TAG_ROW_HOLDER) \u003d\u003d null) row \u003d buildSettingsRowView();\n if (app) bindSettingsRow(row, app);\n return row;\n } catch (e) {\n var fb \u003d convertView;\n if (fb \u003d\u003d null || (fb.getTag \u0026\u0026 fb.getTag(TAG_ROW_HOLDER) \u003d\u003d null)) fb \u003d buildSettingsRowView();\n return fb;\n }\n }\n });\n\n settingsList.setAdapter(adapterSettings);\n\n var listLp \u003d new LinearLayout.LayoutParams(\n LinearLayout.LayoutParams.MATCH_PARENT,\n 0\n );\n listLp.weight \u003d 1;\n settingsBox.addView(settingsList, listLp);\n\n // 底部按钮栏(全选、全不选、关闭)\n var rowBtns \u003d new LinearLayout(ctx);\n rowBtns.setOrientation(LinearLayout.HORIZONTAL);\n rowBtns.setPadding(0, dp(12), 0, 0);\n\n btnAllOn \u003d makeSmallBtn(\"全选\");\n btnAllOn.setOnClickListener(new Packages.android.view.View.OnClickListener({\n onClick: function () { applyAllVisible(1); }\n }));\n\n btnAllOff \u003d makeSmallBtn(\"全不选\");\n btnAllOff.setOnClickListener(new Packages.android.view.View.OnClickListener({\n onClick: function () { applyAllVisible(0); }\n }));\n\n btnSettingsClose \u003d makeSmallBtn(\"关闭\");\n btnSettingsClose.setOnClickListener(new Packages.android.view.View.OnClickListener({\n onClick: function () { hideSettings(); }\n }));\n\n var btnLpA \u003d new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT);\n btnLpA.weight \u003d 1;\n var btnLpB \u003d new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT);\n btnLpB.weight \u003d 1;\n var btnLpC \u003d new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT);\n btnLpC.weight \u003d 1;\n\n btnLpA.rightMargin \u003d dp(8);\n btnLpB.rightMargin \u003d dp(8);\n\n btnAllOn.setLayoutParams(btnLpA);\n btnAllOff.setLayoutParams(btnLpB);\n btnSettingsClose.setLayoutParams(btnLpC);\n\n rowBtns.addView(btnAllOn);\n rowBtns.addView(btnAllOff);\n rowBtns.addView(btnSettingsClose);\n\n settingsBox.addView(rowBtns);\n\n settingsLayer.addView(settingsBox, settingsBoxLp);\n root.addView(settingsLayer);\n\n winLp \u003d new LayoutParams();\n winLp.width \u003d dp(WIN_W_DP);\n winLp.height \u003d dp(WIN_H_DP);\n winLp.gravity \u003d Gravity.LEFT | Gravity.TOP;\n winLp.flags \u003d LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_LAYOUT_IN_SCREEN | LayoutParams.FLAG_LAYOUT_NO_LIMITS;\n winLp.format \u003d Packages.android.graphics.PixelFormat.TRANSLUCENT;\n winLp.type \u003d LayoutParams.TYPE_APPLICATION_OVERLAY;\n winLp.setTitle(\"SX-APPGRID-V2F\");\n\n var pos0 \u003d loadPosFromSettings();\n if (pos0) {\n winLp.x \u003d pos0.x;\n winLp.y \u003d pos0.y;\n } else {\n var x0 \u003d Math.max(0, screenW() - winLp.width - dp(8));\n var y0 \u003d Math.max(0, Math.floor((screenH() - winLp.height) / 2));\n winLp.x \u003d x0;\n winLp.y \u003d y0;\n }\n\n (function () {\n var maxX \u003d Math.max(0, screenW() - winLp.width);\n var maxY \u003d Math.max(0, screenH() - winLp.height);\n winLp.x \u003d clamp(winLp.x, 0, maxX);\n winLp.y \u003d clamp(winLp.y, 0, maxY);\n })();\n\n wm.addView(root, winLp);\n\n // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n // # show anim\n // \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n if (SHOW_PANEL_ANIM) {\n try {\n root.setScaleX(SHOW_PANEL_ANIM_SCALE_FROM);\n root.setScaleY(SHOW_PANEL_ANIM_SCALE_FROM);\n root.setAlpha(0.0);\n root.animate()\n .alpha(0.96)\n .scaleX(1.0)\n .scaleY(1.0)\n .setDuration(SHOW_PANEL_ANIM_MS)\n .start();\n } catch (eAnim) {}\n }\n\n\n uiHandler.postDelayed(new java.lang.Runnable({\n run: function () {\n updateOverlaySizesFromRoot();\n }\n }), 60);\n\n startPreloadOptimized();\n\n return 1;\n });\n }\n\n\n function startFlow() {\n // 立即显示空面板\n uiHandler.post(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n buildAndShowPanelOnUI();\n refreshTabUi();\n }\n }));\n\n // 延迟加载应用数据,避免阻塞UI显示\n wkHandler.postDelayed(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n\n scanVisibleAppsOnWK(function(list) {\n uiHandler.post(new java.lang.Runnable({\n run: function () {\n if (getClosing()) return;\n\n appsAll \u003d list;\n rebuildAppIndex();\n cleanPinned();\n rebuildLists();\n chooseGridDataByTab();\n refreshTabUi();\n\n // 启动后台图标加载\n if (FIRST_SCREEN_PRELOAD \u0026\u0026 ENABLE_LOAD_ICONS) {\n var firstPkgs \u003d buildFirstScreenPkgList();\n preloadFirstScreenAsync(firstPkgs, function() {\n if (adapterGrid) adapterGrid.notifyDataSetChanged();\n });\n }\n\n // 后台预加载更多图标(第二屏)\n preloadMoreIcons();\n }\n }));\n });\n }\n }), 50); // 50ms延迟让UI先渲染\n }\n\n // 预加载更多图标(第二屏)\n function preloadMoreIcons() {\n if (!ENABLE_LOAD_ICONS) return;\n\n wkHandler.postDelayed(new java.lang.Runnable({\n run: function() {\n if (getClosing()) return;\n\n // 预加载第二屏的图标(12-36个)\n var startIdx \u003d FIRST_SCREEN_COUNT;\n var endIdx \u003d Math.min(startIdx + 24, gridData.length);\n\n for (var i \u003d startIdx; i \u003c endIdx; i++) {\n if (getClosing()) return;\n var app \u003d gridData[i];\n if (!app || !app.pkg) continue;\n\n // 检查是否已缓存\n if (iconCache[app.pkg]) continue;\n\n try {\n var icon \u003d pm.getApplicationIcon(app.pkg);\n if (icon) {\n cacheDrawable(app.pkg, icon);\n }\n } catch (e) {}\n }\n\n // 刷新显示\n uiHandler.post(new java.lang.Runnable({\n run: function() {\n if (getClosing()) return;\n if (adapterGrid) adapterGrid.notifyDataSetChanged();\n }\n }));\n }\n }), 300); // 延迟300ms,等首屏加载完成后再加载\n }\n\n\n startFlow();\n\n return JSON.stringify({\n ok: 1,\n msg: \"APPGRID V2F started\",\n scan: ENABLE_SCAN_APPS,\n icons: ENABLE_LOAD_ICONS,\n safeTouch: true,\n preload: { count: PRELOAD_COUNT, delayMs: PRELOAD_DELAY_MS },\n tab: curTab\n });\n})();", "context": "CoroutineContext_UI", "customContextDataKey": { }, "id": "A-3260bbc0-fb9f-4add-8347-c808e9978a7c" }], "id": "SHARED-DA-d86590e6-0411-47a5-b73a-ade3d16f7fa5", "lastUpdateTime": "1773656934630", "createTime": "1766655903685", "author": { "name": "林深见鹿@xin-blog.com" }, "title": "快捷app", "description": "长按应用图标 置顶/取消置顶\n支持搜索", "versionCode": "1", "directActionSetId": "DS-4ca00e3e-39fb-4cc6-83e1-bbf1aad3fc26", "hook": { }, "quit": { } } ###------### {"type":"da"}