reveal method
把 identifier 驱动到已挂载且露出,返回后续写命令该带的 generation
与该走哪条 tap 通道的 reachability。
Implementation
Future<PatchbayInvocation> reveal({
required String identifier,
String? container,
PatchbayRevealDirection direction = PatchbayRevealDirection.both,
int maxSteps = PatchbayRevealBudget.defaultSteps,
int timeoutMs = PatchbayRevealBudget.defaultTimeoutMs,
String? requestId,
}) async {
final String id = requestId ?? _newRequestId();
final DateTime started = DateTime.now();
final List<String> invalid = <String>[
if (identifier.isEmpty) 'identifier',
if (container != null && container.isEmpty) 'container',
if (maxSteps < 1 || maxSteps > PatchbayRevealBudget.maxSteps) 'maxSteps',
if (timeoutMs < 1 || timeoutMs > PatchbayRevealBudget.maxDurationMs)
'timeoutMs',
];
if (invalid.isNotEmpty) {
return _rejected(
id,
'invalidUiArguments',
notice:
'identifier must be non-empty, maxSteps must be '
'1..${PatchbayRevealBudget.maxSteps} and timeoutMs must be '
'1..${PatchbayRevealBudget.maxDurationMs}.',
details: <String, Object?>{'invalid': invalid},
);
}
final PatchbayRevealPolicy? policy = _policy;
if (policy == null) return _rejected(id, 'uiRevealDisabled');
if (!_isAppResumed()) {
return _rejected(
id,
'uiLifecycleNotResumed',
details: patchbayLifecycleDetails(_lifecycleState),
);
}
final SemanticsOwner? owner = await _semantics.ensureOwner();
final SemanticsNode? root = owner?.rootSemanticsNode;
if (owner == null || root == null) {
return _rejected(id, 'uiSemanticsUnavailable');
}
final int beforeTreeRevision = _semantics.treeRevision;
final List<SemanticsNode> targets = patchbaySemanticsNodesWithIdentifier(
root,
identifier,
);
if (targets.length > 1) {
// 目标未挂载是本命令存在的理由,不是错误;多个挂载实例才是。
return _rejected(
id,
'uiSemanticsIdentifierAmbiguous',
details: <String, Object?>{
'identifier': identifier,
'role': 'target',
'matchCount': targets.length,
},
);
}
final SemanticsNode? target = targets.isEmpty ? null : targets.single;
// PB-050-35 / DG-060-05:可达性分类发生在第一次 scroll 派发前,不消耗 step。
final _Admission admission = _admit(
id: id,
root: root,
identifier: identifier,
container: container,
direction: direction,
target: target,
classified: target == null
? null
: patchbayRevealTargetAdmission(
owner: owner,
node: target,
semantics: _semantics,
),
);
if (admission.rejection case final PatchbayInvocation rejection) {
return rejection;
}
if (admission.shortcut case final PatchbayRevealOutcome shortcut) {
return _accepted(
id,
identifier: identifier,
outcome: shortcut,
steps: 0,
containers: const <PatchbayRevealContainerRecord>[],
started: started,
beforeTreeRevision: beforeTreeRevision,
);
}
return _run(
id: id,
started: started,
owner: owner,
policy: policy,
anchor: admission.anchor!,
anchorIdentifier: container,
identifier: identifier,
direction: direction,
maxSteps: maxSteps,
timeoutMs: timeoutMs,
beforeTreeRevision: beforeTreeRevision,
);
}