Skip to content

Commit 03c28c6

Browse files
authored
Use better lints (#704)
* use `package:leancode_lint` * fix most of lint warnings * add CODEOWNERS file
1 parent 6f56881 commit 03c28c6

File tree

9 files changed

+184
-113
lines changed

9 files changed

+184
-113
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @bartekpacia

README.md

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ following messages.
249249
<string name="flutter_downloader_notification_paused">Download paused</string>
250250
```
251251

252-
You can learn more about localization on Android [here][4]).
252+
You can learn more about localization on Android [here][4].
253253

254254
### Install .apk files
255255

@@ -300,7 +300,7 @@ final taskId = await FlutterDownloader.enqueue(
300300
### Update download progress
301301

302302
```dart
303-
FlutterDownloader.registerCallback(callback); // callback is a top-level or static function
303+
await FlutterDownloader.registerCallback(callback); // callback is a top-level or static function
304304
```
305305

306306
**Important**
@@ -445,16 +445,12 @@ plugin is missing some feature.
445445

446446
Pull request are also very welcome!
447447

448-
[fluttercommunity_badge]:
449-
https://fluttercommunity.dev/_github/header/flutter_downloader
448+
[fluttercommunity_badge]: https://fluttercommunity.dev/_github/header/flutter_downloader
450449
[fluttercommunity_link]: https://github.com/fluttercommunity/community
451450
[pub_badge]: https://img.shields.io/pub/v/flutter_downloader.svg
452451
[pub_link]: https://pub.dartlang.org/packages/flutter_downloader
453-
[work_manager]:
454-
https://developer.android.com/topic/libraries/architecture/workmanager
455-
[url_session_download_task]:
456-
https://developer.apple.com/documentation/foundation/nsurlsessiondownloadtask?language=objc
457-
[android_9_cleartext_traffic]:
458-
https://medium.com/@son.rommer/fix-cleartext-traffic-error-in-android-9-pie-2f4e9e2235e6
452+
[work_manager]: https://developer.android.com/topic/libraries/architecture/workmanager
453+
[url_session_download_task]: https://developer.apple.com/documentation/foundation/nsurlsessiondownloadtask?language=objc
454+
[android_9_cleartext_traffic]: https://medium.com/@son.rommer/fix-cleartext-traffic-error-in-android-9-pie-2f4e9e2235e6
459455
[3]: https://medium.com/@guerrix/info-plist-localization-ad5daaea732a
460456
[4]: https://developer.android.com/training/basics/supporting-devices/languages

analysis_options.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
include: package:flutter_lints/flutter.yaml
1+
include: package:leancode_lint/analysis_options_package.yaml

lib/flutter_downloader.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@
1313
library flutter_downloader;
1414

1515
export 'src/downloader.dart';
16+
export 'src/exceptions.dart';
1617
export 'src/models.dart';

lib/src/callback_dispatcher.dart

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,31 @@ import 'package:flutter/widgets.dart';
66

77
import 'models.dart';
88

9-
// pragma annotation is needed to avoid tree shaking in release mode
10-
// https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md
9+
/// Pragma annotation is needed to avoid tree shaking in release mode. See
10+
/// https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md
1111
@pragma('vm:entry-point')
1212
void callbackDispatcher() {
13-
const MethodChannel backgroundChannel =
14-
MethodChannel('vn.hunghd/downloader_background');
13+
const backgroundChannel = MethodChannel('vn.hunghd/downloader_background');
1514

1615
WidgetsFlutterBinding.ensureInitialized();
1716

18-
backgroundChannel.setMethodCallHandler((MethodCall call) async {
19-
final args = call.arguments;
20-
final handle = CallbackHandle.fromRawHandle(args[0]);
21-
final callback = PluginUtilities.getCallbackFromHandle(handle);
22-
23-
if (callback == null) {
24-
// ignore: avoid_print
25-
print('fatal error: could not find callback');
26-
exit(1);
27-
}
28-
29-
final String id = args[1];
30-
final int status = args[2];
31-
final int progress = args[3];
32-
33-
callback(id, DownloadTaskStatus(status), progress);
34-
});
35-
36-
backgroundChannel.invokeMethod('didInitializeDispatcher');
17+
backgroundChannel
18+
..setMethodCallHandler((call) async {
19+
final args = call.arguments as List<dynamic>;
20+
final handle = CallbackHandle.fromRawHandle(args[0] as int);
21+
final callback = PluginUtilities.getCallbackFromHandle(handle);
22+
23+
if (callback == null) {
24+
// ignore: avoid_print
25+
print('fatal error: could not find callback');
26+
exit(1);
27+
}
28+
29+
final id = args[1] as String;
30+
final status = args[2] as int;
31+
final progress = args[3] as int;
32+
33+
callback(id, DownloadTaskStatus(status), progress);
34+
})
35+
..invokeMethod<void>('didInitializeDispatcher');
3736
}

lib/src/downloader.dart

Lines changed: 110 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import 'dart:async';
2+
import 'dart:convert';
23
import 'dart:io';
34
import 'dart:ui';
45

56
import 'package:flutter/services.dart';
67
import 'package:flutter/widgets.dart';
8+
import 'package:flutter_downloader/src/exceptions.dart';
79

810
import 'callback_dispatcher.dart';
911
import 'models.dart';
@@ -21,9 +23,14 @@ class FlutterDownloader {
2123
static const _channel = MethodChannel('vn.hunghd/downloader');
2224

2325
static bool _initialized = false;
26+
27+
/// Whether the plugin is initialized. The plugin must be initialized before
28+
/// use.
2429
static bool get initialized => _initialized;
2530

2631
static bool _debug = false;
32+
33+
/// If true, more logs are printed.
2734
static bool get debug => _debug;
2835

2936
/// Initializes the plugin. This must be called before any other method.
@@ -47,10 +54,10 @@ class FlutterDownloader {
4754
WidgetsFlutterBinding.ensureInitialized();
4855

4956
final callback = PluginUtilities.getCallbackHandle(callbackDispatcher)!;
50-
await _channel.invokeMethod('initialize', <dynamic>[
57+
await _channel.invokeMethod<void>('initialize', <dynamic>[
5158
callback.toRawHandle(),
52-
debug ? 1 : 0,
53-
ignoreSsl ? 1 : 0,
59+
if (debug) 1 else 0,
60+
if (ignoreSsl) 1 else 0,
5461
]);
5562

5663
_initialized = true;
@@ -87,62 +94,78 @@ class FlutterDownloader {
8794
required String url,
8895
required String savedDir,
8996
String? fileName,
90-
Map<String, String>? headers,
97+
Map<String, String> headers = const {},
9198
bool showNotification = true,
9299
bool openFileFromNotification = true,
93100
bool requiresStorageNotLow = true,
94101
bool saveInPublicStorage = false,
95102
}) async {
96103
assert(_initialized, 'plugin flutter_downloader is not initialized');
97-
assert(Directory(savedDir).existsSync(), "savedDir does not exist");
98-
99-
StringBuffer headerBuilder = StringBuffer();
100-
if (headers != null) {
101-
headerBuilder.write('{');
102-
headerBuilder.writeAll(
103-
headers.entries.map((entry) => '"${entry.key}": "${entry.value}"'),
104-
',',
105-
);
106-
headerBuilder.write('}');
107-
}
104+
assert(Directory(savedDir).existsSync(), 'savedDir does not exist');
105+
108106
try {
109-
String? taskId = await _channel.invokeMethod('enqueue', {
107+
final taskId = await _channel.invokeMethod<String>('enqueue', {
110108
'url': url,
111109
'saved_dir': savedDir,
112110
'file_name': fileName,
113-
'headers': headerBuilder.toString(),
111+
'headers': jsonEncode(headers),
114112
'show_notification': showNotification,
115113
'open_file_from_notification': openFileFromNotification,
116114
'requires_storage_not_low': requiresStorageNotLow,
117115
'save_in_public_storage': saveInPublicStorage,
118116
});
117+
118+
if (taskId == null) {
119+
throw const FlutterDownloaderException(
120+
message: '`enqueue` returned null taskId',
121+
);
122+
}
123+
119124
return taskId;
120-
} on PlatformException catch (e) {
121-
_log('Download task is failed with reason(${e.message})');
122-
return null;
125+
} on FlutterDownloaderException catch (err) {
126+
_log('Failed to enqueue. Reason: ${err.message}');
127+
} on PlatformException catch (err) {
128+
_log('Failed to enqueue. Reason: ${err.message}');
123129
}
130+
131+
return null;
124132
}
125133

126134
/// Loads all tasks from SQLite database.
127135
static Future<List<DownloadTask>?> loadTasks() async {
128136
assert(_initialized, 'plugin flutter_downloader is not initialized');
129137

130138
try {
131-
List<dynamic> result = await _channel.invokeMethod('loadTasks');
132-
return result
133-
.map((item) => DownloadTask(
134-
taskId: item['task_id'],
135-
status: DownloadTaskStatus(item['status']),
136-
progress: item['progress'],
137-
url: item['url'],
138-
filename: item['file_name'],
139-
savedDir: item['saved_dir'],
140-
timeCreated: item['time_created']))
141-
.toList();
142-
} on PlatformException catch (e) {
143-
_log(e.message);
139+
final result = await _channel.invokeMethod<List<dynamic>>('loadTasks');
140+
141+
if (result == null) {
142+
throw const FlutterDownloaderException(
143+
message: '`loadTasks` returned null',
144+
);
145+
}
146+
147+
return result.map(
148+
(dynamic item) {
149+
// item as Map<String, dynamic>; // throws
150+
151+
return DownloadTask(
152+
taskId: item['task_id'] as String,
153+
status: DownloadTaskStatus(item['status'] as int),
154+
progress: item['progress'] as int,
155+
url: item['url'] as String,
156+
filename: item['file_name'] as String?,
157+
savedDir: item['saved_dir'] as String,
158+
timeCreated: item['time_created'] as int,
159+
);
160+
},
161+
).toList();
162+
} on FlutterDownloaderException catch (err) {
163+
_log('Failed to load tasks. Reason: ${err.message}');
164+
} on PlatformException catch (err) {
165+
_log(err.message);
144166
return null;
145167
}
168+
return null;
146169
}
147170

148171
/// Loads tasks from SQLite database using raw [query].
@@ -167,20 +190,34 @@ class FlutterDownloader {
167190
assert(_initialized, 'plugin flutter_downloader is not initialized');
168191

169192
try {
170-
List<dynamic> result = await _channel
171-
.invokeMethod('loadTasksWithRawQuery', {'query': query});
172-
return result
173-
.map((item) => DownloadTask(
174-
taskId: item['task_id'],
175-
status: DownloadTaskStatus(item['status']),
176-
progress: item['progress'],
177-
url: item['url'],
178-
filename: item['file_name'],
179-
savedDir: item['saved_dir'],
180-
timeCreated: item['time_created']))
181-
.toList();
182-
} on PlatformException catch (e) {
183-
_log(e.message);
193+
final result = await _channel.invokeMethod<List<dynamic>>(
194+
'loadTasksWithRawQuery',
195+
{'query': query},
196+
);
197+
198+
if (result == null) {
199+
throw const FlutterDownloaderException(
200+
message: '`loadTasksWithRawQuery` returned null',
201+
);
202+
}
203+
204+
return result.map(
205+
(dynamic item) {
206+
// item as Map<String, dynamic>; // throws
207+
208+
return DownloadTask(
209+
taskId: item['task_id'] as String,
210+
status: DownloadTaskStatus(item['status'] as int),
211+
progress: item['progress'] as int,
212+
url: item['url'] as String,
213+
filename: item['file_name'] as String?,
214+
savedDir: item['saved_dir'] as String,
215+
timeCreated: item['time_created'] as int,
216+
);
217+
},
218+
).toList();
219+
} on PlatformException catch (err) {
220+
_log('Failed to loadTasksWithRawQuery. Reason: ${err.message}');
184221
return null;
185222
}
186223
}
@@ -190,9 +227,9 @@ class FlutterDownloader {
190227
assert(_initialized, 'plugin flutter_downloader is not initialized');
191228

192229
try {
193-
return await _channel.invokeMethod('cancel', {'task_id': taskId});
194-
} on PlatformException catch (e) {
195-
_log(e.message);
230+
await _channel.invokeMethod<void>('cancel', {'task_id': taskId});
231+
} on PlatformException catch (err) {
232+
_log(err.message);
196233
}
197234
}
198235

@@ -202,8 +239,8 @@ class FlutterDownloader {
202239

203240
try {
204241
return await _channel.invokeMethod('cancelAll');
205-
} on PlatformException catch (e) {
206-
_log(e.message);
242+
} on PlatformException catch (err) {
243+
_log(err.message);
207244
}
208245
}
209246

@@ -292,23 +329,32 @@ class FlutterDownloader {
292329
}
293330
}
294331

295-
/// Opens the downloaded file with [taskId]. Returns true if the downloaded
296-
/// file can be opened, false otherwise.
332+
/// Opens the file downloaded by download task with [taskId]. Returns true if
333+
/// the downloaded file can be opened, false otherwise.
297334
///
298335
/// On Android, there are two requirements for opening the file:
299336
/// - The file must be saved in external storage where other applications have
300337
/// permission to read the file
301-
/// - There is at least 1 application that can read the files of type of the
302-
/// file.
338+
/// - There must be at least 1 application that can read the files of type of
339+
/// the file.
303340
static Future<bool> open({required String taskId}) async {
304341
assert(_initialized, 'plugin flutter_downloader is not initialized');
305342

306343
try {
307-
return await _channel.invokeMethod('open', {'task_id': taskId});
308-
} on PlatformException catch (e) {
309-
_log(e.message);
344+
final result = await _channel.invokeMethod<bool>(
345+
'open',
346+
{'task_id': taskId},
347+
);
348+
349+
if (result == null) {
350+
throw const FlutterDownloaderException(message: '`open` returned null');
351+
}
352+
} on PlatformException catch (err) {
353+
_log('Failed to open downloaded file. Reason: ${err.message}');
310354
return false;
311355
}
356+
357+
return false;
312358
}
313359

314360
/// Registers a [callback] to track the status and progress of a download
@@ -354,7 +400,10 @@ class FlutterDownloader {
354400
/// send.send([id, status, progress]);
355401
///}
356402
///```
357-
static registerCallback(DownloadCallback callback, {int step = 10}) {
403+
static Future<void> registerCallback(
404+
DownloadCallback callback, {
405+
int step = 10,
406+
}) async {
358407
assert(_initialized, 'plugin flutter_downloader is not initialized');
359408

360409
final callbackHandle = PluginUtilities.getCallbackHandle(callback);
@@ -368,7 +417,7 @@ class FlutterDownloader {
368417
'step size is not in the inclusive <0;100> range',
369418
);
370419

371-
_channel.invokeMethod(
420+
await _channel.invokeMethod<void>(
372421
'registerCallback',
373422
<dynamic>[callbackHandle!.toRawHandle(), step],
374423
);

lib/src/exceptions.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/// Thrown when something bad happens on the Dart side of the flutter_downloader
2+
/// plugin.
3+
class FlutterDownloaderException implements Exception {
4+
/// Creates a new [FlutterDownloaderException].
5+
const FlutterDownloaderException({required this.message});
6+
7+
/// Description of the problem.
8+
final String message;
9+
}

0 commit comments

Comments
 (0)