main
dart 104 lines 3.04 KB
Raw
1 import 'dart:async';
2
3 import 'package:flutter/foundation.dart';
4 import 'package:flutter/services.dart';
5 import 'package:flutter_riverpod/flutter_riverpod.dart';
6
7 import '../logger/app_logger.dart';
8
9 enum AndroidAssistantStatus { unsupported, available, active, manual, unknown }
10
11 class AndroidAssistantService {
12 AndroidAssistantService({MethodChannel? channel, bool? supportedPlatform})
13 : _channel = channel ?? const MethodChannel(_channelName),
14 _supportedPlatformOverride = supportedPlatform;
15
16 static const _channelName = 'localmind/android_assistant';
17
18 final MethodChannel _channel;
19 final bool? _supportedPlatformOverride;
20 final StreamController<void> _invocations =
21 StreamController<void>.broadcast();
22
23 bool _initialized = false;
24 bool _disposed = false;
25
26 bool get isSupportedPlatform =>
27 _supportedPlatformOverride ??
28 (!kIsWeb && defaultTargetPlatform == TargetPlatform.android);
29
30 Stream<void> get invocations => _invocations.stream;
31
32 Future<void> initialize() async {
33 if (!isSupportedPlatform || _initialized || _disposed) return;
34 _initialized = true;
35
36 _channel.setMethodCallHandler(_handleNativeCall);
37
38 try {
39 final pending =
40 await _channel.invokeMethod<bool>('consumePendingInvocation') ??
41 false;
42 if (pending && !_disposed) {
43 _invocations.add(null);
44 }
45 } on PlatformException catch (error) {
46 Log.error('Android assistant initialization failed: $error');
47 }
48 }
49
50 Future<AndroidAssistantStatus> getStatus() async {
51 if (!isSupportedPlatform) return AndroidAssistantStatus.unsupported;
52
53 try {
54 final value = await _channel.invokeMethod<String>('getAssistantStatus');
55 return switch (value) {
56 'active' => AndroidAssistantStatus.active,
57 'available' => AndroidAssistantStatus.available,
58 'manual' => AndroidAssistantStatus.manual,
59 'unsupported' => AndroidAssistantStatus.unsupported,
60 _ => AndroidAssistantStatus.unknown,
61 };
62 } on PlatformException catch (error) {
63 Log.error('Unable to read Android assistant status: $error');
64 return AndroidAssistantStatus.unknown;
65 }
66 }
67
68 Future<bool> requestRole() async {
69 if (!isSupportedPlatform) return false;
70 return await _channel.invokeMethod<bool>('requestAssistantRole') ?? false;
71 }
72
73 Future<void> openSettings() async {
74 if (!isSupportedPlatform) return;
75 await _channel.invokeMethod<void>('openAssistantSettings');
76 }
77
78 Future<dynamic> _handleNativeCall(MethodCall call) async {
79 if (call.method != 'assistantInvoked') {
80 throw MissingPluginException(
81 'Unknown Android assistant call: ${call.method}',
82 );
83 }
84
85 if (!_disposed) {
86 _invocations.add(null);
87 }
88 }
89
90 Future<void> dispose() async {
91 if (_disposed) return;
92 _disposed = true;
93 _channel.setMethodCallHandler(null);
94 await _invocations.close();
95 }
96 }
97
98 final androidAssistantServiceProvider = Provider<AndroidAssistantService>((
99 ref,
100 ) {
101 final service = AndroidAssistantService();
102 ref.onDispose(service.dispose);
103 return service;
104 });