Line data Source code
1 : import 'dart:async';
2 : import 'dart:typed_data';
3 :
4 : import 'package:matrix/encryption.dart';
5 : import 'package:matrix/matrix.dart';
6 : import 'package:matrix/src/utils/compute_callback.dart';
7 :
8 : /// provides native implementations for demanding arithmetic operations
9 : /// in order to prevent the UI from blocking
10 : ///
11 : /// possible implementations might be:
12 : /// - native code
13 : /// - another Dart isolate
14 : /// - a web worker
15 : /// - a dummy implementations
16 : ///
17 : /// Rules for extension (important for [noSuchMethod] implementations)
18 : /// - always only accept exactly *one* positioned argument
19 : /// - catch the corresponding case in [NativeImplementations.noSuchMethod]
20 : /// - always write a dummy implementations
21 : abstract class NativeImplementations {
22 90 : const NativeImplementations();
23 :
24 : /// a dummy implementation executing all calls in the same thread causing
25 : /// the UI to likely freeze
26 : static const dummy = NativeImplementationsDummy();
27 :
28 : FutureOr<RoomKeys> generateUploadKeys(
29 : GenerateUploadKeysArgs args, {
30 : bool retryInDummy = true,
31 : });
32 :
33 : FutureOr<Uint8List> keyFromPassphrase(
34 : KeyFromPassphraseArgs args, {
35 : bool retryInDummy = true,
36 : });
37 :
38 : FutureOr<Uint8List?> decryptFile(
39 : EncryptedFile file, {
40 : bool retryInDummy = true,
41 : });
42 :
43 : FutureOr<MatrixImageFileResizedResponse?> shrinkImage(
44 : MatrixImageFileResizeArguments args, {
45 : bool retryInDummy = false,
46 : });
47 :
48 : FutureOr<MatrixImageFileResizedResponse?> calcImageMetadata(
49 : Uint8List bytes, {
50 : bool retryInDummy = false,
51 : });
52 :
53 : /// this implementation will catch any non-implemented method
54 0 : @override
55 : dynamic noSuchMethod(Invocation invocation) {
56 0 : final dynamic argument = invocation.positionalArguments.single;
57 0 : final memberName = invocation.memberName.toString().split('"')[1];
58 :
59 0 : Logs().d(
60 : 'Missing implementations of Client.nativeImplementations.$memberName. '
61 : 'You should consider implementing it. '
62 : 'Fallback from NativeImplementations.dummy used.',
63 : );
64 : switch (memberName) {
65 : // we need to pass the futures right through or we will run into type errors later!
66 0 : case 'generateUploadKeys':
67 : // ignore: discarded_futures
68 0 : return dummy.generateUploadKeys(argument);
69 0 : case 'keyFromPassphrase':
70 : // ignore: discarded_futures
71 0 : return dummy.keyFromPassphrase(argument);
72 0 : case 'decryptFile':
73 : // ignore: discarded_futures
74 0 : return dummy.decryptFile(argument);
75 0 : case 'shrinkImage':
76 0 : return dummy.shrinkImage(argument);
77 0 : case 'calcImageMetadata':
78 0 : return dummy.calcImageMetadata(argument);
79 : default:
80 0 : return super.noSuchMethod(invocation);
81 : }
82 : }
83 : }
84 :
85 : class NativeImplementationsDummy extends NativeImplementations {
86 90 : const NativeImplementationsDummy();
87 :
88 1 : @override
89 : Future<Uint8List?> decryptFile(
90 : EncryptedFile file, {
91 : bool retryInDummy = true,
92 : }) {
93 1 : return decryptFileImplementation(file);
94 : }
95 :
96 4 : @override
97 : Future<RoomKeys> generateUploadKeys(
98 : GenerateUploadKeysArgs args, {
99 : bool retryInDummy = true,
100 : }) async {
101 4 : return generateUploadKeysImplementation(args);
102 : }
103 :
104 2 : @override
105 : Future<Uint8List> keyFromPassphrase(
106 : KeyFromPassphraseArgs args, {
107 : bool retryInDummy = true,
108 : }) {
109 2 : return generateKeyFromPassphrase(args);
110 : }
111 :
112 3 : @override
113 : MatrixImageFileResizedResponse? shrinkImage(
114 : MatrixImageFileResizeArguments args, {
115 : bool retryInDummy = false,
116 : }) {
117 3 : return MatrixImageFile.resizeImplementation(args);
118 : }
119 :
120 2 : @override
121 : MatrixImageFileResizedResponse? calcImageMetadata(
122 : Uint8List bytes, {
123 : bool retryInDummy = false,
124 : }) {
125 2 : return MatrixImageFile.calcMetadataImplementation(bytes);
126 : }
127 : }
128 :
129 : /// a [NativeImplementations] based on Flutter's `compute` function
130 : ///
131 : /// this implementations simply wraps the given [compute] function around
132 : /// the implementation of [NativeImplementations.dummy]
133 : class NativeImplementationsIsolate extends NativeImplementations {
134 : /// pass by Flutter's compute function here
135 : final ComputeCallback compute;
136 : final Future<void> Function()? vodozemacInit;
137 :
138 0 : NativeImplementationsIsolate(
139 : this.compute, {
140 : /// To generate upload keys, vodozemac needs to be initialized in the isolate.
141 : this.vodozemacInit,
142 : });
143 :
144 0 : Future<T> runInBackground<T, U>(
145 : FutureOr<T> Function(U arg) function,
146 : U arg,
147 : ) async {
148 0 : final compute = this.compute;
149 0 : return await compute(function, arg);
150 : }
151 :
152 0 : @override
153 : Future<Uint8List?> decryptFile(
154 : EncryptedFile file, {
155 : bool retryInDummy = true,
156 : }) {
157 0 : return runInBackground<Uint8List?, EncryptedFile>(
158 0 : (EncryptedFile args) async {
159 0 : await vodozemacInit?.call();
160 0 : return NativeImplementations.dummy.decryptFile(args);
161 : },
162 : file,
163 : );
164 : }
165 :
166 0 : @override
167 : Future<RoomKeys> generateUploadKeys(
168 : GenerateUploadKeysArgs args, {
169 : bool retryInDummy = true,
170 : }) async {
171 0 : return runInBackground<RoomKeys, GenerateUploadKeysArgs>(
172 0 : (GenerateUploadKeysArgs args) async {
173 0 : await vodozemacInit?.call();
174 0 : return NativeImplementations.dummy.generateUploadKeys(args);
175 : },
176 : args,
177 : );
178 : }
179 :
180 0 : @override
181 : Future<Uint8List> keyFromPassphrase(
182 : KeyFromPassphraseArgs args, {
183 : bool retryInDummy = true,
184 : }) {
185 0 : return runInBackground<Uint8List, KeyFromPassphraseArgs>(
186 0 : (KeyFromPassphraseArgs args) async {
187 0 : await vodozemacInit?.call();
188 0 : return NativeImplementations.dummy.keyFromPassphrase(args);
189 : },
190 : args,
191 : );
192 : }
193 :
194 0 : @override
195 : Future<MatrixImageFileResizedResponse?> shrinkImage(
196 : MatrixImageFileResizeArguments args, {
197 : bool retryInDummy = false,
198 : }) {
199 0 : return runInBackground<MatrixImageFileResizedResponse?,
200 : MatrixImageFileResizeArguments>(
201 0 : NativeImplementations.dummy.shrinkImage,
202 : args,
203 : );
204 : }
205 :
206 0 : @override
207 : FutureOr<MatrixImageFileResizedResponse?> calcImageMetadata(
208 : Uint8List bytes, {
209 : bool retryInDummy = false,
210 : }) {
211 0 : return runInBackground<MatrixImageFileResizedResponse?, Uint8List>(
212 0 : NativeImplementations.dummy.calcImageMetadata,
213 : bytes,
214 : );
215 : }
216 : }
|