Line data Source code
1 : part of './services.nativechannels.dart'; 2 : 3 : /// Manage our channels instances and listeners by [targetMethod] 4 : /// callback registered. 5 : class CallBacksController implements ICallBacksController { 6 : static const String _tag = 'CallBacksController'; 7 : 8 : /// CallBackMethod<T> 9 : /// here we can't use this. 10 : final _callbacks = <CallBackMethod<MirrorMethodCall, Error>>{}; 11 : 12 6 : static final CallBacksController _singleton = CallBacksController._internal(); 13 : 14 2 : factory CallBacksController() { 15 2 : return _singleton; 16 : } 17 : 18 2 : CallBacksController._internal(); 19 : 20 : int _nextCallBackReferenceId = 0; 21 : 22 2 : int get lastAddedcallBackReferenceId => _nextCallBackReferenceId; 23 : 24 1 : @override 25 2 : int get registeredCallbacksCount => _callbacks.length; 26 : 27 : @override 28 1 : Future<dynamic> platformCallBackMethodHandlerSetup(MethodCall call) async { 29 : try { 30 2 : if (_callbacks.isEmpty) { 31 : // throw Exception('No callback registered.'); 32 : // TODO(v): maybe there is a delay between hosts callbacks invoke 33 0 : log('SUPOSE TO: \nthrow Exception(\'No callback registered.\');', 34 : level: 2000); 35 0 : return Future.value('[Platform info]: No callback registered.'); 36 : } 37 : 38 1 : switch (call.method) { 39 1 : case PlatformEntrypoint.setupMethodHandler: 40 1 : case PlatformEntrypoint.platformCallBackMethodHandler: 41 2 : Message message = Message.fromBuffer(call.arguments); 42 : 43 3 : if (message.header.intent == 44 : Header_CommunicationType.CANCELATION) { 45 3 : disposeCallBack(message.header.callBackReferenceId); 46 : break; 47 : } 48 : 49 2 : int id = message.header.callBackReferenceId; 50 : 51 1 : if (id == 0) { 52 0 : throw Exception('Invalid [callback id] value.'); 53 : } 54 : 55 : /// On this case we can call a global dispose on host. 56 : /// Means that there are more instances inside our native channel. 57 5 : if (!_callbacks.any((e) => e.id == id)) { 58 : // TODO(v): maybe there is a delay between hosts callbacks invoke 59 0 : log('SUPOSE TO: \nthrow Exception(\'Invalid [callback id]. Is this callback disposed?\');', 60 : level: 2000); 61 0 : return Future.value('[Platform info]: callback id not found'); 62 : // throw Exception( 63 : // 'Invalid [callback id]. Is this callback disposed?'); 64 : } 65 : 66 : final dynamicMethod = 67 5 : _callbacks.firstWhere((element) => element.id == id); 68 : 69 : // TODO(v): function references doesn't throw explicit runtime exceptions on message.apply 70 : // final arguments = await message.apply(); 71 : 72 2 : dynamicMethod.call( 73 1 : message.methodCall, 74 2 : message.payload.error, 75 : ); 76 : 77 : break; 78 : default: 79 0 : log('CallBacksController, platformCallBackMethodHandlerSetup -> IGNORING: ${call.method}'); 80 : break; 81 : } 82 : } catch (e, s) { 83 0 : log('CallBacksController, platformCallBackMethodHandlerSetup\n$call', 84 : name: _tag, stackTrace: s, error: e); 85 : rethrow; 86 : } 87 : } 88 : 89 1 : @override 90 : bool isAlreadyRegistered({required String targetMethod}) => 91 2 : _callbacks.any((e) => e.message.header.targetMethod == targetMethod); 92 : 93 : /// If [listenerCancelation] method is provided 94 : /// It will dispose the current [targetMethod] listener. 95 : /// Can be used like a [restart] on our actual callback listener, 96 : /// and avoid duplicated listeners. 97 1 : Future<void> disposeActiveCallbackListener({ 98 : required String targetMethod, 99 : required CancelListening Function(Message msg) listenerCancelation, 100 : }) async { 101 : CallBackMethod<dynamic, dynamic> callBack; 102 : 103 : try { 104 1 : callBack = _callbacks 105 6 : .firstWhere((e) => e.message.header.targetMethod == targetMethod); 106 1 : } on StateError catch (_) { 107 : return; 108 0 : } on Exception catch (e, s) { 109 0 : log('disposeActiveCallbackListener:', 110 : name: _tag, stackTrace: s, error: e); 111 : rethrow; 112 : } 113 : 114 : try { 115 6 : await listenerCancelation.call(callBack.message).call().then((_) { 116 2 : disposeCallBack(callBack.id); 117 : }); 118 : } catch (e, s) { 119 0 : log('CancelListening, listenerCancelation\n', 120 : name: _tag, stackTrace: s, error: e); 121 0 : throw Exception('CancelListening listenerCancelation() fail.'); 122 : } 123 : } 124 : 125 : /// If call [setCallBack] to a [targetMethod] already registered, 126 : /// then will dispose the actual callback listener before continue. 127 1 : @override 128 : void setCallBack({ 129 : required CallBackMethod<MirrorMethodCall, Error> call, 130 : }) { 131 4 : if (isAlreadyRegistered(targetMethod: call.message.header.targetMethod)) { 132 0 : throw Exception( 133 : 'targetMethod: [call.message.header.targetMethod] already registered.'); 134 : } 135 : 136 2 : _nextCallBackReferenceId++; 137 : 138 4 : call.message.header.callBackReferenceId = _nextCallBackReferenceId; 139 : 140 2 : _callbacks.add(call); 141 : 142 6 : log('CallBacksController, setCallBack -> $_nextCallBackReferenceId : ${call.message.header.targetMethod}'); 143 : } 144 : 145 1 : @override 146 : void disposeCallBack(int callBackReferenceId) { 147 2 : log('CallBacksController, disposeCallBack -> $callBackReferenceId'); 148 7 : _callbacks.removeWhere((e) => e.message.header.callBackReferenceId == callBackReferenceId); 149 : } 150 : }