@@ -33,9 +33,10 @@ class _LogMessage {
33
33
34
34
class _FirmwareEntry {
35
35
final String name;
36
- final String url;
36
+ final String fileName;
37
+ final String zipUrl;
37
38
38
- _FirmwareEntry (this .name, this .url );
39
+ _FirmwareEntry (this .name, this .fileName, this .zipUrl );
39
40
}
40
41
41
42
class _FlashPinecilPageState extends State <FlashPinecilPage > {
@@ -49,23 +50,23 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
49
50
int _progress = 0 ;
50
51
bool _errorOccured = false ;
51
52
late Future <List <_FirmwareEntry >> _firmwareListFuture;
52
- String ? _firmwareType ;
53
+ _FirmwareEntry ? _firmwareToFlash ;
53
54
final _dio = Dio ();
54
- String ? _pinecilZipUrl;
55
55
56
56
bool get _isPinecilConnected => (_pinecilState == _PinecilState .Connected ||
57
57
_pinecilState == _PinecilState .ConnectedNoDriver );
58
58
final _firmwarePathController = TextEditingController ();
59
59
60
- void startUSBTimer () {
61
- _usbTimer = Timer .periodic (const Duration (seconds: 1 ), updatePinecilStatus);
60
+ void _startUSBTimer () {
61
+ _usbTimer =
62
+ Timer .periodic (const Duration (seconds: 1 ), _updatePinecilStatus);
62
63
}
63
64
64
65
@override
65
66
void initState () {
66
67
super .initState ();
67
- startUSBTimer ();
68
- _firmwareListFuture = getLatestFirmwares ();
68
+ _startUSBTimer ();
69
+ _firmwareListFuture = _getLatestFirmwares ();
69
70
}
70
71
71
72
@override
@@ -75,46 +76,71 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
75
76
_firmwarePathController.dispose ();
76
77
}
77
78
78
- Future <List <_FirmwareEntry >> getLatestFirmwares () async {
79
+ Future <void > _parseJsonMetadata (List <_FirmwareEntry > firmwaresList,
80
+ ArchiveFile jsonArchive, String zipUrl) async {
81
+ final metadata = jsonDecode (utf8.decode (jsonArchive.content));
82
+ final list = List <_FirmwareEntry >.empty (growable: true );
83
+ for (String firmwareFile in metadata['contents' ].keys) {
84
+ if (firmwareFile.contains ('.hex' )) continue ;
85
+ final firmwareInfo = metadata['contents' ][firmwareFile];
86
+ list.add (
87
+ _FirmwareEntry (
88
+ "IronOS ${metadata ['release' ]} - ${firmwareInfo ['language_name' ]}" ,
89
+ firmwareFile,
90
+ zipUrl),
91
+ );
92
+ }
93
+ list.sort ((a, b) => a.fileName.compareTo (b.fileName));
94
+ firmwaresList.addAll (list);
95
+ }
96
+
97
+ Future <List <_FirmwareEntry >> _getLatestFirmwares () async {
98
+ final list = List <_FirmwareEntry >.empty (growable: true );
79
99
try {
80
100
final githubResponse = await _dio
81
101
.get ("https://api.github.com/repos/Ralim/IronOS/releases/latest" );
82
102
if (githubResponse.statusCode != 200 ) {
83
103
throw "Failed to fetch Github releases" ;
84
104
}
85
105
List <dynamic > assets = githubResponse.data['assets' ];
86
- _pinecilZipUrl = assets.singleWhere ((element) =>
106
+ final pinecilZipUrl = assets.singleWhere ((element) =>
87
107
element['name' ] == 'Pinecil.zip' )? ['browser_download_url' ];
108
+ final pinecilMultilangZipUrl = assets.singleWhere ((element) =>
109
+ element['name' ] == 'Pinecil_multi-lang.zip' )? ['browser_download_url' ];
88
110
final metadata =
89
111
assets.singleWhere ((element) => element['name' ] == "metadata.zip" );
90
112
final metadataRequest = await _dio.get (metadata['browser_download_url' ],
91
113
options: Options (responseType: ResponseType .bytes));
92
114
final metadataZip = ZipDecoder ().decodeBytes (metadataRequest.data);
115
+ final pinecilMultilangJsonFile = metadataZip
116
+ .singleWhere ((element) => element.name == "Pinecil_multi-lang.json" );
117
+ await _parseJsonMetadata (
118
+ list, pinecilMultilangJsonFile, pinecilMultilangZipUrl);
93
119
final pinecilJsonFile =
94
120
metadataZip.singleWhere ((element) => element.name == "Pinecil.json" );
95
- final pinecilMetadata = jsonDecode (utf8.decode (pinecilJsonFile.content));
121
+ await _parseJsonMetadata (list, pinecilJsonFile, pinecilZipUrl);
122
+ /*final pinecilMetadata = jsonDecode(utf8.decode(pinecilJsonFile.content));
96
123
return List<_FirmwareEntry>.generate(
97
124
pinecilMetadata['contents'].keys.length, (index) {
98
125
final firmwareFile = pinecilMetadata['contents'].keys.toList()[index];
99
126
final firmwareInfo = pinecilMetadata['contents'][firmwareFile];
100
127
return _FirmwareEntry(
101
128
"IronOS ${pinecilMetadata['release']} - ${firmwareInfo['language_name']}",
102
129
firmwareFile);
103
- });
130
+ });*/
104
131
} catch (ex) {
105
132
print (ex);
106
133
}
107
- return List < _FirmwareEntry >. empty () ;
134
+ return list ;
108
135
}
109
136
110
- void updatePinecilStatus (Timer timer) {
137
+ void _updatePinecilStatus (Timer timer) {
111
138
final devicesList = libUSB.getDevicesList ();
112
139
if (_previousDevicesCount != devicesList.devices.length) {
113
140
_previousDevicesCount = devicesList.devices.length;
114
141
_pinecilState = _PinecilState .Disconnected ;
115
142
for (var device in devicesList.devices) {
116
143
var descriptor = libUSB.getDeviceDescriptor (device);
117
- print (descriptor.idVendor.toRadixString (16 ) + " / " + descriptor.idProduct.toRadixString (16 ));
118
144
if (descriptor.idVendor == 0x28E9 && descriptor.idProduct == 0x0189 ) {
119
145
try {
120
146
final deviceHandle = libUSB.open (device);
@@ -141,19 +167,20 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
141
167
devicesList.free ();
142
168
}
143
169
144
- void copyLog () {
170
+ void _copyLog () {
145
171
String logData = _log.map ((e) => e.message).join ('\n ' );
146
172
FlutterClipboard .copy (logData);
147
173
ScaffoldMessenger .of (context).showSnackBar (const SnackBar (
148
174
content: Text ("The log has been copied" ),
149
175
));
150
176
}
151
177
152
- void flashFirmware ({String ? firmwarePath, String ? firmwareURL}) async {
178
+ void _flashFirmware (
179
+ {String ? firmwarePath, _FirmwareEntry ? firmwareEntry}) async {
153
180
String filePath = "" ;
154
181
155
182
// region Custom Firmware Check
156
- if (firmwareURL == null ) {
183
+ if (firmwareEntry == null ) {
157
184
if (firmwarePath == null ) {
158
185
ScaffoldMessenger .of (context).showSnackBar (const SnackBar (
159
186
content: Text ("You need to specify custom firmware path" ),
@@ -204,14 +231,15 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
204
231
stderr.addStream(dfuUtil.stderr); */
205
232
206
233
// region Download Firmware
207
- if (firmwareURL != null ) {
234
+ if (firmwareEntry != null ) {
208
235
setState (() {
209
236
_state = "Downloading firmware... 0%" ;
210
- _log.add (_LogMessage ("Downloading firmware " + firmwareURL, false ));
237
+ _log.add (_LogMessage (
238
+ "Downloading firmware " + firmwareEntry.fileName, false ));
211
239
});
212
240
try {
213
241
final firmwaresZipResponse = await _dio.get (
214
- _pinecilZipUrl ! ,
242
+ firmwareEntry.zipUrl ,
215
243
onReceiveProgress: (count, total) => setState (() {
216
244
_state =
217
245
"Downloading firmware... ${((count / total ) * 100.0 ).round ()}%" ;
@@ -223,7 +251,7 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
223
251
ZipDecoder ().decodeBytes (firmwaresZipResponse.data);
224
252
225
253
final firmwareZipFile = firmwaresZip.files
226
- .singleWhere ((element) => element.name == firmwareURL );
254
+ .singleWhere ((element) => element.name == firmwareEntry.fileName );
227
255
final firmwareFile = File ("$currentWorkingDirectory /_tmpfirm.dfu" );
228
256
await firmwareFile.create (recursive: true );
229
257
await firmwareFile.writeAsBytes (firmwareZipFile.content);
@@ -324,7 +352,7 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
324
352
});
325
353
}
326
354
327
- Step connectStep (BuildContext context) {
355
+ Step _connectStep (BuildContext context) {
328
356
return Step (
329
357
isActive: _currentStep == 0 ,
330
358
state: _currentStep > 0 ? StepState .complete : StepState .indexed,
@@ -350,7 +378,7 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
350
378
);
351
379
}
352
380
353
- Step selectFirmwareStep (BuildContext context) {
381
+ Step _selectFirmwareStep (BuildContext context) {
354
382
return Step (
355
383
isActive: _currentStep == 1 ,
356
384
state: _currentStep > 1 ? StepState .complete : StepState .indexed,
@@ -365,26 +393,26 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
365
393
return Row (
366
394
children: [
367
395
Expanded (
368
- child: DropdownButtonFormField <String >(
369
- value: _firmwareType ,
396
+ child: DropdownButtonFormField <_FirmwareEntry ? >(
397
+ value: _firmwareToFlash ,
370
398
isDense: true ,
371
399
items: [
372
- const DropdownMenuItem <String >(
400
+ const DropdownMenuItem <_FirmwareEntry ? >(
373
401
child: Text ("Custom" ),
374
402
value: null ,
375
403
),
376
404
if (snapshot.hasData)
377
405
...snapshot.data!
378
- .map <DropdownMenuItem <String >>(
379
- (e) => DropdownMenuItem <String >(
406
+ .map <DropdownMenuItem <_FirmwareEntry ? >>(
407
+ (e) => DropdownMenuItem <_FirmwareEntry ? >(
380
408
child: Text (e.name),
381
- value: e.url ,
409
+ value: e,
382
410
),
383
411
)
384
412
.toList ()
385
413
],
386
414
onChanged: (item) =>
387
- setState (() => _firmwareType = item),
415
+ setState (() => _firmwareToFlash = item),
388
416
decoration:
389
417
const InputDecoration (labelText: 'Firmware Type' ),
390
418
),
@@ -408,15 +436,15 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
408
436
children: [
409
437
Flexible (
410
438
child: TextField (
411
- enabled: _firmwareType == null ,
439
+ enabled: _firmwareToFlash == null ,
412
440
controller: _firmwarePathController,
413
441
decoration:
414
442
const InputDecoration (labelText: 'Path to the firmware' ),
415
443
),
416
444
),
417
445
const SizedBox (width: 8 ),
418
446
ElevatedButton (
419
- onPressed: _firmwareType == null
447
+ onPressed: _firmwareToFlash == null
420
448
? () async {
421
449
var result = await FilePicker .platform.pickFiles (
422
450
type: FileType .custom,
@@ -435,9 +463,9 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
435
463
),
436
464
const SizedBox (height: 18 ),
437
465
ElevatedButton (
438
- onPressed: () => flashFirmware (
466
+ onPressed: () => _flashFirmware (
439
467
firmwarePath: _firmwarePathController.text,
440
- firmwareURL : _firmwareType ,
468
+ firmwareEntry : _firmwareToFlash ,
441
469
),
442
470
child: const Text ('Update' ),
443
471
),
@@ -446,7 +474,7 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
446
474
);
447
475
}
448
476
449
- Step updateStep (BuildContext context) {
477
+ Step _updateStep (BuildContext context) {
450
478
return Step (
451
479
isActive: _currentStep == 2 ,
452
480
state: _currentStep > 2 ? StepState .complete : StepState .indexed,
@@ -479,7 +507,7 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
479
507
headerBuilder: (context, isExpanded) => ListTile (
480
508
title: Text ('Log' ),
481
509
trailing: IconButton (
482
- onPressed: copyLog ,
510
+ onPressed: _copyLog ,
483
511
icon: Icon (Icons .copy),
484
512
tooltip: 'Copy log to clipboard' ,
485
513
),
@@ -515,7 +543,7 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
515
543
);
516
544
}
517
545
518
- Step unplugStep (BuildContext context) {
546
+ Step _unplugStep (BuildContext context) {
519
547
return Step (
520
548
isActive: _currentStep == 3 ,
521
549
title: const Text ('Unplug' ),
@@ -524,7 +552,8 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
524
552
);
525
553
}
526
554
527
- Widget stepperControlsBuilder (BuildContext context, ControlsDetails details) {
555
+ Widget _stepperControlsBuilder (
556
+ BuildContext context, ControlsDetails details) {
528
557
if (details.currentStep == 0 ) {
529
558
if (_pinecilState == _PinecilState .Error ) {
530
559
return const Text (
@@ -545,12 +574,12 @@ class _FlashPinecilPageState extends State<FlashPinecilPage> {
545
574
? StepperType .vertical
546
575
: StepperType .horizontal,
547
576
currentStep: _currentStep,
548
- controlsBuilder: stepperControlsBuilder ,
577
+ controlsBuilder: _stepperControlsBuilder ,
549
578
steps: [
550
- connectStep (context),
551
- selectFirmwareStep (context),
552
- updateStep (context),
553
- unplugStep (context)
579
+ _connectStep (context),
580
+ _selectFirmwareStep (context),
581
+ _updateStep (context),
582
+ _unplugStep (context)
554
583
],
555
584
),
556
585
);
0 commit comments