feat: refine AI navigation and accessibility recovery
This commit is contained in:
@@ -62,9 +62,39 @@ class RecognitionDiagnostic {
|
||||
}
|
||||
}
|
||||
|
||||
enum AccessibilityConnectionState {
|
||||
unauthorized,
|
||||
reconnecting,
|
||||
connected,
|
||||
disconnected;
|
||||
|
||||
static AccessibilityConnectionState parse(
|
||||
Object? value, {
|
||||
required bool authorized,
|
||||
required bool connected,
|
||||
}) {
|
||||
return switch (value?.toString()) {
|
||||
'reconnecting' => AccessibilityConnectionState.reconnecting,
|
||||
'connected' => AccessibilityConnectionState.connected,
|
||||
'disconnected' => AccessibilityConnectionState.disconnected,
|
||||
'unauthorized' => AccessibilityConnectionState.unauthorized,
|
||||
_ =>
|
||||
!authorized
|
||||
? unauthorized
|
||||
: connected
|
||||
? AccessibilityConnectionState.connected
|
||||
: disconnected,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class RecognitionStatus {
|
||||
final bool accessibilityAuthorized;
|
||||
final bool accessibilityConnected;
|
||||
final AccessibilityConnectionState accessibilityConnectionState;
|
||||
final DateTime? accessibilityLastConnectedAt;
|
||||
final DateTime? accessibilityLastDisconnectedAt;
|
||||
final DateTime? recognitionProcessStartedAt;
|
||||
final bool notificationAuthorized;
|
||||
final bool notificationConnected;
|
||||
final bool postNotificationsGranted;
|
||||
@@ -81,6 +111,11 @@ class RecognitionStatus {
|
||||
const RecognitionStatus({
|
||||
required this.accessibilityAuthorized,
|
||||
required this.accessibilityConnected,
|
||||
this.accessibilityConnectionState =
|
||||
AccessibilityConnectionState.unauthorized,
|
||||
this.accessibilityLastConnectedAt,
|
||||
this.accessibilityLastDisconnectedAt,
|
||||
this.recognitionProcessStartedAt,
|
||||
required this.notificationAuthorized,
|
||||
required this.notificationConnected,
|
||||
required this.postNotificationsGranted,
|
||||
@@ -101,10 +136,28 @@ class RecognitionStatus {
|
||||
final settings = rawSettings == null || rawSettings.isEmpty
|
||||
? const <String, dynamic>{}
|
||||
: jsonDecode(rawSettings) as Map<String, dynamic>;
|
||||
final accessibilityAuthorized =
|
||||
value['accessibilityAuthorized'] as bool? ?? false;
|
||||
final accessibilityConnected =
|
||||
value['accessibilityConnected'] as bool? ?? false;
|
||||
DateTime? epochDate(String key) {
|
||||
final epoch = (value[key] as num?)?.toInt() ?? 0;
|
||||
return epoch <= 0 ? null : DateTime.fromMillisecondsSinceEpoch(epoch);
|
||||
}
|
||||
|
||||
return RecognitionStatus(
|
||||
accessibilityAuthorized:
|
||||
value['accessibilityAuthorized'] as bool? ?? false,
|
||||
accessibilityConnected: value['accessibilityConnected'] as bool? ?? false,
|
||||
accessibilityAuthorized: accessibilityAuthorized,
|
||||
accessibilityConnected: accessibilityConnected,
|
||||
accessibilityConnectionState: AccessibilityConnectionState.parse(
|
||||
value['accessibilityConnectionState'],
|
||||
authorized: accessibilityAuthorized,
|
||||
connected: accessibilityConnected,
|
||||
),
|
||||
accessibilityLastConnectedAt: epochDate('accessibilityLastConnectedAt'),
|
||||
accessibilityLastDisconnectedAt: epochDate(
|
||||
'accessibilityLastDisconnectedAt',
|
||||
),
|
||||
recognitionProcessStartedAt: epochDate('recognitionProcessStartedAt'),
|
||||
notificationAuthorized: value['notificationAuthorized'] as bool? ?? false,
|
||||
notificationConnected: value['notificationConnected'] as bool? ?? false,
|
||||
postNotificationsGranted:
|
||||
@@ -130,6 +183,19 @@ class RecognitionStatus {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
bool get accessibilityNeedsRecovery =>
|
||||
accessibilityAuthorized &&
|
||||
!accessibilityConnected &&
|
||||
accessibilityConnectionState == AccessibilityConnectionState.disconnected;
|
||||
|
||||
String get accessibilityConnectionLabel =>
|
||||
switch (accessibilityConnectionState) {
|
||||
AccessibilityConnectionState.unauthorized => '未授权',
|
||||
AccessibilityConnectionState.reconnecting => '正在重连',
|
||||
AccessibilityConnectionState.connected => '已连接',
|
||||
AccessibilityConnectionState.disconnected => '服务未连接',
|
||||
};
|
||||
}
|
||||
|
||||
class RecognitionCandidate {
|
||||
@@ -403,6 +469,31 @@ class ScreenshotChannel {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<RecognitionStatus> waitForAccessibilityConnection({
|
||||
Duration timeout = const Duration(seconds: 5),
|
||||
Duration interval = const Duration(milliseconds: 500),
|
||||
}) async {
|
||||
var status = await recognitionStatus();
|
||||
final recognitionEnabled =
|
||||
status.accessibilityEvents || status.aiScreenshot;
|
||||
if (!recognitionEnabled ||
|
||||
!status.accessibilityAuthorized ||
|
||||
status.accessibilityConnected) {
|
||||
return status;
|
||||
}
|
||||
final deadline = DateTime.now().add(timeout);
|
||||
while (DateTime.now().isBefore(deadline)) {
|
||||
await Future<void>.delayed(interval);
|
||||
status = await recognitionStatus();
|
||||
if (!status.accessibilityAuthorized ||
|
||||
status.accessibilityConnected ||
|
||||
!(status.accessibilityEvents || status.aiScreenshot)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static Future<bool> clearRecognitionDiagnostic() async {
|
||||
try {
|
||||
return await _channel.invokeMethod<bool>('clearRecognitionDiagnostic') ??
|
||||
|
||||
Reference in New Issue
Block a user