项目文件夹

文件
wehub-resource-sync 2114b14ee0
Sync main into demo / sync (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:26 +08:00

517 行
19 KiB
TypeScript

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
import type { ActivityInstance, AppId, OSState } from './types';
import type { ActivityResult, AppIntentFilter, IntentPayload } from './types/manifest';
import { SIMULATOR_CONFIG } from './data';
import PackageManagerService from './PackageManagerService';
import { getActiveTask, getTaskTopActivity } from './taskUtils';
import { AppNavigatorRegistry } from './AppNavigatorRegistry';
type ChooserListener = (state: IntentChooserState) => void;
export interface IntentChooserState {
open: boolean;
intent: IntentPayload | null;
matches: { appId: AppId; filter: AppIntentFilter }[];
}
export interface StartActivityDeps {
getState: () => OSState;
nextActivityId: () => string;
allocRequestCode: (callerActivityId: string, callback: (result: ActivityResult) => void) => number;
pushActivity: (taskId: string, activity: ActivityInstance) => void;
navigateToActivity: (activityId: string, route: string, opts?: { fallbackAppId?: AppId; replace?: boolean }) => void;
}
export interface StartActivityPlainDeps {
getState: () => OSState;
nextActivityId: () => string;
pushActivity: (taskId: string, activity: ActivityInstance) => void;
popActivity: (taskId: string) => void;
navigateToActivity: (activityId: string, route: string, opts?: { fallbackAppId?: AppId; replace?: boolean }) => void;
launchApp: (appId: AppId) => void;
markExternalRoute: (appId: AppId) => void;
setActivityIntent: (taskId: string, activityId: string, intent: IntentPayload) => void;
}
const chooserListeners = new Set<ChooserListener>();
let chooserResolver: ((appId: AppId | null) => void) | null = null;
let chooserState: IntentChooserState = {
open: false,
intent: null,
matches: [],
};
function buildIntentDebugStack(tag: string): string {
try {
throw new Error(tag);
} catch (error) {
if (!(error instanceof Error) || !error.stack) return 'n/a';
return error.stack
.split('\n')
.slice(1, 5)
.map((line) => line.trim())
.join(' <- ');
}
}
function emitChooser() {
for (const listener of chooserListeners) listener(chooserState);
}
function setChooser(next: IntentChooserState) {
chooserState = next;
emitChooser();
}
function matchesType(filterType: string | undefined, intentType: string | undefined) {
if (!filterType) return true;
if (!intentType) return false;
if (filterType.endsWith('/*')) return intentType.startsWith(filterType.slice(0, -1));
return filterType === intentType;
}
function matchesScheme(filterScheme: string | undefined, intentScheme: string | undefined) {
if (!filterScheme) return true;
return filterScheme === intentScheme;
}
function pickTargetFilter(appId: AppId, intent: IntentPayload) {
const targetManifest = PackageManagerService.getPackageInfo(appId);
const filters = targetManifest?.intentFilters ?? [];
return filters.find(
(f) => f.action === intent.action
&& matchesScheme(f.scheme, intent.scheme)
&& matchesType(f.type, intent.type),
);
}
function ensureCallerQueryVisible(callerAppId: AppId, intent: IntentPayload) {
const callerManifest = PackageManagerService.getPackageInfo(callerAppId);
if (!callerManifest) return;
const queries = callerManifest.queries ?? [];
const queryOk = queries.some((q) =>
q.action === intent.action
&& matchesScheme(q.scheme, intent.scheme)
&& matchesType(q.type, intent.type),
);
if (!queryOk) {
console.warn(
`[OS] startActivityForResult: ${callerAppId} does not declare queries for action=${intent.action} scheme=${intent.scheme ?? '-'} type=${intent.type ?? '-'}`
);
}
}
function requestIntentChoice(
intent: IntentPayload,
matches: { appId: AppId; filter: AppIntentFilter }[],
): Promise<AppId | null> {
return new Promise((resolve) => {
if (chooserResolver) {
resolve(null);
return;
}
chooserResolver = resolve;
setChooser({ open: true, intent, matches });
});
}
function launchResolvedIntentForResult(
appId: AppId,
callerAppId: AppId,
intent: IntentPayload,
callback: (result: ActivityResult) => void,
deps: StartActivityDeps,
): boolean {
if (!PackageManagerService.isInstalled(appId)) {
console.error(`[OS] startActivityForResult: Invalid appId: ${appId}`);
return false;
}
ensureCallerQueryVisible(callerAppId, intent);
const targetFilter = pickTargetFilter(appId, intent);
if (!targetFilter && !intent.route) {
console.error(
`[OS] startActivityForResult: 无法解析 intent — ${appId} 没有匹配的 intentFilter,`
+ `且 intent 未提供 route (action=${intent.action} scheme=${intent.scheme ?? '-'} type=${intent.type ?? '-'})`,
);
return false;
}
if (!targetFilter) {
console.warn(
`[OS] startActivityForResult: ${appId} has no intentFilter for action=${intent.action} scheme=${intent.scheme ?? '-'} type=${intent.type ?? '-'}, using intent.route fallback`,
);
}
const activeTask = getActiveTask(deps.getState());
if (!activeTask) {
console.error('[OS] startActivityForResult: No active task');
return false;
}
const callerActivity = getTaskTopActivity(activeTask);
if (!callerActivity) {
console.error('[OS] startActivityForResult: No caller activity');
return false;
}
const requestCode = deps.allocRequestCode(callerActivity.activityId, callback);
// 调用方 intent.route 优先于 manifest filter.route。
// 真机对应:filter 决定哪个 Activity 接收,调用方通过 intent extras / data 决定 Activity 内部细节;
// 我们把 route 当成"caller hint"暴露出来,让调用方一次性指明目标子页(如 Settings → FM /category/images),
// 避免 OS 先 navigate('/') 又被 App-side dispatcher replace 到子页的双跳竞态。
// 缺省(未指定 intent.route)时仍走 filter.route。
const baseRoute = intent.route ?? targetFilter?.route ?? '/';
const newActivity: ActivityInstance = {
activityId: deps.nextActivityId(),
appId,
initialRoute: baseRoute,
intent,
requestCode,
callerActivityId: callerActivity.activityId,
};
console.log(
`[OSDBG] startActivityForResult caller=${callerAppId} target=${appId} route=${baseRoute} `
+ `action=${intent.action} scheme=${intent.scheme ?? '-'} type=${intent.type ?? '-'} `
+ `stack=${buildIntentDebugStack('startActivityForResult')}`,
);
console.log(`[OS] startActivityForResult: ${callerAppId}${appId} route=${baseRoute}`);
deps.pushActivity(activeTask.taskId, newActivity);
deps.navigateToActivity(newActivity.activityId, baseRoute, { fallbackAppId: newActivity.appId });
return true;
}
function launchResolvedIntent(
appId: AppId,
callerAppId: AppId,
intent: IntentPayload,
newTask: boolean,
deps: StartActivityPlainDeps,
): boolean {
if (!PackageManagerService.isInstalled(appId)) {
console.error(`[OS] startActivity: Invalid appId: ${appId}`);
return false;
}
ensureCallerQueryVisible(callerAppId, intent);
const targetFilter = pickTargetFilter(appId, intent);
if (!targetFilter && !intent.route) {
console.error(
`[OS] startActivity: 无法解析 intent — ${appId} 没有匹配的 intentFilter,`
+ `且 intent 未提供 route (action=${intent.action} scheme=${intent.scheme ?? '-'} type=${intent.type ?? '-'})`,
);
return false;
}
// 同 forResult 路径:intent.route 优先(caller hint 一次性指明目标子页),缺省走 filter.route。
const baseRoute = intent.route ?? targetFilter?.route ?? '/';
const enrichedIntent: IntentPayload = {
...intent,
data: { ...intent.data, __callerAppId: callerAppId },
};
// 接收方声明 launchMode='singleTask' 时,强制 promote 到 newTask 路径,对应真机
// "Activity 上的 launchMode 由接收方决定,凌驾于调用方 flag" 的语义:
// 即使调用方没传 FLAG_ACTIVITY_NEW_TASK,singleTask Activity 也始终落在自己的 task 里。
// 典型例子:12306 不传 newTask 调 startActivity(ACTION_VIEW + scheme=sms),SMS 仍然进入自己的独立 task。
const launchMode = targetFilter?.launchMode ?? 'standard';
const effectiveNewTask = newTask || launchMode === 'singleTask';
console.log(
`[OSDBG] startActivity caller=${callerAppId} target=${appId} route=${baseRoute} newTask=${String(newTask)} `
+ `effectiveNewTask=${String(effectiveNewTask)} launchMode=${launchMode} `
+ `action=${intent.action} scheme=${intent.scheme ?? '-'} type=${intent.type ?? '-'} `
+ `stack=${buildIntentDebugStack('startActivity')}`,
);
if (effectiveNewTask) {
const callerTaskId = getActiveTask(deps.getState())?.taskId;
const taskExisted = deps.getState().tasks.some((t) => t.rootAppId === appId);
// singleTask + 已存在 Task:清栈到 root + 重置 root 历史 + 重新投递 intent。
// 对应真机 Activity 上 launchMode="singleTask" 的语义。
if (launchMode === 'singleTask' && taskExisted) {
const existingTask = deps.getState().tasks.find((t) => t.rootAppId === appId);
if (!existingTask || existingTask.stack.length === 0) return false;
// Pop 掉 root 之上所有 Activity(含 foreign-task 借栈来的)。
while (true) {
const cur = deps.getState().tasks.find((t) => t.rootAppId === appId);
if (!cur || cur.stack.length <= 1) break;
deps.popActivity(cur.taskId);
}
const refreshedTask = deps.getState().tasks.find((t) => t.rootAppId === appId);
if (!refreshedTask || refreshedTask.stack.length === 0) return false;
const rootActivity = refreshedTask.stack[0];
// 把 enrichedIntent 投递到 root activityShareForwardPage 通过 getIntentPayload 读取)。
deps.setActivityIntent(refreshedTask.taskId, rootActivity.activityId, enrichedIntent);
// 把 wechat Task 切到前台。LAUNCH_APP 不会动其它 task — Gallery Task 仍保留在后台。
deps.launchApp(appId);
// 重置 MemoryRouter 历史到 ['/', baseRoute]:先 popToRoot 退回 '/',再 push baseRoute。
requestAnimationFrame(() => {
// rAF 间隙里 task 可能被外部 closeTask 移除(如用户立刻在 recents 里划掉)。
// 此时 navigator 可能尚未走完 React 异步 unregister,仍能拿到引用,但不该再操作已关闭的 Task。
const stillExists = deps.getState().tasks.some((t) => t.rootAppId === appId);
if (!stillExists) {
console.warn(`[OS] startActivity(singleTask): ${appId} task closed before rAF — aborting nav reset`);
return;
}
const nav = AppNavigatorRegistry.get(appId);
if (!nav) {
console.warn(`[OS] startActivity(singleTask): navigator not registered for ${appId}`);
return;
}
if (nav.popToRoot) {
nav.popToRoot();
} else {
// Older AppNavigator without popToRoot — fallback to replace
nav.navigate('/', { replace: true });
}
nav.navigate(baseRoute, { replace: false });
});
console.log(`[OS] startActivity: ${callerAppId}${appId} route=${baseRoute} (singleTask, reused)`);
return true;
}
deps.launchApp(appId);
if (!taskExisted) {
deps.markExternalRoute(appId);
}
// Zustand dispatch is synchronous — locate the target activity immediately.
// Reverse-search for the activity belonging to appId (the stack top may be
// another app's activity pushed via same-task mode).
const postState = deps.getState();
const task = postState.tasks.find((t) => t.rootAppId === appId);
if (!task || task.stack.length === 0) return false;
if (!taskExisted) {
const targetActivity =
[...task.stack].reverse().find((act) => act.appId === appId)
?? task.stack[task.stack.length - 1];
deps.setActivityIntent(task.taskId, targetActivity.activityId, enrichedIntent);
const targetActivityId = targetActivity.activityId;
// singleTask 新建 Task 时用 push 模式,使根 Activity 历史为 ['/', baseRoute],
// 完成后 replace 到结果路由形成 ['/', resultRoute],返回键能回到主页 '/'.
// 默认 standard 走 replace(历史为 [baseRoute]),保持 ACTION_PAY 等"finish 即回调用方"的精确语义。
const replaceMode = launchMode !== 'singleTask';
requestAnimationFrame(() => {
deps.navigateToActivity(targetActivityId, baseRoute, { replace: replaceMode, fallbackAppId: appId });
});
} else {
const newActivity: ActivityInstance = {
activityId: deps.nextActivityId(),
appId,
initialRoute: baseRoute,
launchedByTaskId: callerTaskId,
intent: enrichedIntent,
};
deps.pushActivity(task.taskId, newActivity);
requestAnimationFrame(() => {
deps.navigateToActivity(newActivity.activityId, baseRoute, { fallbackAppId: appId });
});
}
console.log(`[OS] startActivity: ${callerAppId}${appId} route=${baseRoute} (newTask)`);
return true;
}
// Same-task mode: push new Activity into caller's Task
const activeTask = getActiveTask(deps.getState());
if (!activeTask) {
console.error('[OS] startActivity: No active task');
return false;
}
const newActivity: ActivityInstance = {
activityId: deps.nextActivityId(),
appId,
initialRoute: baseRoute,
intent: enrichedIntent,
};
console.log(`[OS] startActivity: ${callerAppId}${appId} route=${baseRoute} (sameTask)`);
deps.pushActivity(activeTask.taskId, newActivity);
deps.navigateToActivity(newActivity.activityId, baseRoute, { fallbackAppId: newActivity.appId });
return true;
}
export const IntentResolver = {
getState(): IntentChooserState {
return chooserState;
},
subscribe(listener: ChooserListener): () => void {
chooserListeners.add(listener);
listener(chooserState);
return () => chooserListeners.delete(listener);
},
chooseIntentActivity(appId: AppId): void {
const resolver = chooserResolver;
chooserResolver = null;
setChooser({ open: false, intent: null, matches: [] });
if (resolver) resolver(appId);
},
cancelIntentChooser(): void {
const resolver = chooserResolver;
chooserResolver = null;
setChooser({ open: false, intent: null, matches: [] });
if (resolver) resolver(null);
},
startActivityForResult(
appIdOrIntent: AppId | string | IntentPayload,
intentOrCallback: IntentPayload | ((result: ActivityResult) => void) | undefined,
callbackOrUndefined: ((result: ActivityResult) => void) | undefined,
deps: StartActivityDeps,
): boolean {
let appId: AppId | null = null;
let intent: IntentPayload;
let callback: (result: ActivityResult) => void;
if (typeof appIdOrIntent === 'string') {
appId = appIdOrIntent as AppId;
intent = intentOrCallback as IntentPayload;
callback = callbackOrUndefined!;
} else if (typeof appIdOrIntent === 'object' && appIdOrIntent && 'action' in appIdOrIntent) {
intent = appIdOrIntent;
callback = intentOrCallback as (result: ActivityResult) => void;
const matches = PackageManagerService.queryIntentActivities(intent);
if (matches.length === 0) {
console.error(
`[OS] startActivityForResult: 隐式解析无匹配 — action=${intent.action} scheme=${intent.scheme ?? '-'} type=${intent.type ?? '-'}`
);
return false;
}
if (matches.length > 1 && SIMULATOR_CONFIG.intent.chooserEnabled) {
if (chooserResolver) {
return false;
}
const activeTask = getActiveTask(deps.getState());
const top = getTaskTopActivity(activeTask);
const callerAppId = top?.appId ?? null;
if (!callerAppId) {
console.error('[OS] startActivityForResult: No active app');
return false;
}
void requestIntentChoice(intent, matches).then((chosen) => {
if (!chosen) {
requestAnimationFrame(() => callback({ resultCode: 'CANCELED' }));
return;
}
launchResolvedIntentForResult(chosen, callerAppId, intent, callback, deps);
});
return true;
}
appId = matches[0].appId;
console.log(`[OS] startActivityForResult: 隐式解析 → ${appId} (共 ${matches.length} 个匹配)`);
} else {
console.error('[OS] startActivityForResult: 参数格式错误');
return false;
}
if (typeof callback !== 'function') {
console.error('[OS] startActivityForResult: callback is required');
return false;
}
if (!appId) {
console.error('[OS] startActivityForResult: Invalid appId');
return false;
}
const activeTask = getActiveTask(deps.getState());
if (!activeTask) {
console.error('[OS] startActivityForResult: No active task');
return false;
}
const callerActivity = getTaskTopActivity(activeTask);
if (!callerActivity) {
console.error('[OS] startActivityForResult: No caller activity');
return false;
}
const callerAppId = callerActivity.appId;
return launchResolvedIntentForResult(appId, callerAppId, intent, callback, deps);
},
startActivity(
appIdOrIntent: AppId | string | IntentPayload,
intentOrOptions: IntentPayload | { newTask?: boolean } | undefined,
optionsOrUndefined: { newTask?: boolean } | undefined,
deps: StartActivityPlainDeps,
): boolean {
let appId: AppId | null = null;
let intent: IntentPayload;
let newTask = false;
if (typeof appIdOrIntent === 'string') {
appId = appIdOrIntent as AppId;
intent = intentOrOptions as IntentPayload;
newTask = !!optionsOrUndefined?.newTask;
} else if (typeof appIdOrIntent === 'object' && appIdOrIntent && 'action' in appIdOrIntent) {
intent = appIdOrIntent;
const opts = intentOrOptions as { newTask?: boolean } | undefined;
newTask = !!opts?.newTask;
const matches = PackageManagerService.queryIntentActivities(intent);
if (matches.length === 0) {
console.error(
`[OS] startActivity: 隐式解析无匹配 — action=${intent.action} scheme=${intent.scheme ?? '-'} type=${intent.type ?? '-'}`
);
return false;
}
if (matches.length > 1 && SIMULATOR_CONFIG.intent.chooserEnabled) {
if (chooserResolver) return false;
const activeTask = getActiveTask(deps.getState());
const top = getTaskTopActivity(activeTask);
const callerAppId = top?.appId ?? null;
if (!callerAppId) {
console.error('[OS] startActivity: No active app');
return false;
}
void requestIntentChoice(intent, matches).then((chosen) => {
if (!chosen) return;
launchResolvedIntent(chosen, callerAppId, intent, newTask, deps);
});
return true;
}
appId = matches[0].appId;
console.log(`[OS] startActivity: 隐式解析 → ${appId} (共 ${matches.length} 个匹配)`);
} else {
console.error('[OS] startActivity: 参数格式错误');
return false;
}
if (!appId) {
console.error('[OS] startActivity: Invalid appId');
return false;
}
const activeTask = getActiveTask(deps.getState());
if (!activeTask) {
console.error('[OS] startActivity: No active task');
return false;
}
const callerActivity = getTaskTopActivity(activeTask);
if (!callerActivity) {
console.error('[OS] startActivity: No caller activity');
return false;
}
return launchResolvedIntent(appId, callerActivity.appId, intent, newTask, deps);
},
};
export default IntentResolver;