Skip to content

Commit bf0ffca

Browse files
committed
fix(webui): 修复应用分身重复显示
1 parent 443a838 commit bf0ffca

2 files changed

Lines changed: 29 additions & 4 deletions

File tree

src/webui/src/components/AppsScreen.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ defineExpose({
608608
<div class="apps-virtual-inner" :style="{ transform: `translateY(${visibleRange.offsetY}px)` }">
609609
<div
610610
v-for="app in visibleApps"
611-
:key="app.packageName + app.uid"
611+
:key="`${app.userId}:${app.packageName}`"
612612
class="app-item-card"
613613
@click="toggleAppProxy(app)">
614614

src/webui/src/utils/ksu.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,31 @@ export interface PackagesInfo {
116116
uid: number; // 应用 UID
117117
}
118118

119+
const ANDROID_UID_PER_USER = 100000;
120+
121+
/**
122+
* 合并 KernelSU 在应用分身场景下返回的重复记录。
123+
* 同一包名在不同 Android 用户中仍分别保留,与 userId:packageName 配置格式一致。
124+
*/
125+
const normalizePackagesInfo = (infos: PackagesInfo[]): PackagesInfo[] => {
126+
const unique = new Map<string, PackagesInfo>();
127+
128+
infos.forEach(info => {
129+
if (!info || typeof info.packageName !== 'string' || !Number.isInteger(info.uid) || info.uid < 0) return;
130+
131+
const packageName = info.packageName.trim();
132+
if (!packageName) return;
133+
134+
const userId = Math.floor(info.uid / ANDROID_UID_PER_USER);
135+
const key = `${userId}:${packageName}`;
136+
if (!unique.has(key)) {
137+
unique.set(key, { ...info, packageName });
138+
}
139+
});
140+
141+
return Array.from(unique.values());
142+
};
143+
119144
// ===================================================================
120145
// 三、模块 CLI 调用
121146
// ===================================================================
@@ -278,7 +303,7 @@ export const getAppIconUrl = (packageName: string): string => {
278303
/**
279304
* 获取已安装应用列表(含名称 / 版本 / UID / 是否系统应用)。
280305
* 真机直接走 KernelSU 原生 listPackages + getPackagesInfo(无 pm fork、有类型)。
281-
* 注:原生 API 仅返回主用户应用;不含工作资料 / 应用分身等多用户条目
306+
* KernelSU 在部分应用分身环境中可能返回重复条目,结果会按用户与包名归一化
282307
* 非真机环境返回 mock 列表。
283308
* @param filter 'user' | 'system' | 'all'(默认 'all')
284309
* @returns Promise<PackagesInfo[]>
@@ -304,8 +329,8 @@ export const getAppPackagesList = async (filter: 'user' | 'system' | 'all' = 'al
304329
const infos = getPackagesInfo(names) as PackagesInfo[];
305330
if (!infos) return [];
306331

307-
// 3. 过滤掉无效条目(getPackagesInfo 对找不到的包返回 {packageName, error})
308-
return infos.filter(info => info && info.packageName && typeof info.uid === 'number');
332+
// 3. 过滤无效条目,并合并同一用户下重复的应用分身记录
333+
return normalizePackagesInfo(infos);
309334
} catch (err) {
310335
console.error('Failed to query apps via KernelSU listPackages/getPackagesInfo:', err);
311336
return [];

0 commit comments

Comments
 (0)