1818// ignore_for_file: use_build_context_synchronously
1919
2020import 'dart:convert' ;
21- import 'dart:io' ;
2221import 'dart:math' ;
2322
2423import 'package:auto_size_text/auto_size_text.dart' ;
2524import 'package:flutter/material.dart' ;
2625import 'package:http/http.dart' as http;
2726import 'package:logging/logging.dart' ;
2827import 'package:phosphor_flutter/phosphor_flutter.dart' ;
28+ import 'package:provider/provider.dart' ;
29+ import 'package:orion/util/providers/orion_update_provider.dart' ;
2930
3031import 'package:orion/glasser/glasser.dart' ;
3132import 'package:orion/pubspec.dart' ;
@@ -55,9 +56,11 @@ class UpdateScreenState extends State<UpdateScreen> with SafeSetStateMixin {
5556 String _currentVersion = '' ;
5657 String _release = 'BRANCH_dev' ;
5758 String _assetUrl = '' ;
59+ String _repo = 'Open-Resin-Alliance' ;
5860
5961 final Logger _logger = Logger ('UpdateScreen' );
6062 final OrionConfig _config = OrionConfig ();
63+ // Update dialog state is handled by OrionUpdateProvider
6164
6265 @override
6366 void initState () {
@@ -68,6 +71,7 @@ class UpdateScreenState extends State<UpdateScreen> with SafeSetStateMixin {
6871 _betaUpdatesOverride =
6972 _config.getFlag ('releaseOverride' , category: 'developer' );
7073 _release = _config.getString ('overrideRelease' , category: 'developer' );
74+ _repo = _config.getString ('overrideRepo' , category: 'developer' );
7175 _logger.info ('Firmware spoofing enabled: $_isFirmwareSpoofingEnabled ' );
7276 _logger.info ('Beta updates override enabled: $_betaUpdatesOverride ' );
7377 _logger.info ('Release channel override: $_release ' );
@@ -93,8 +97,8 @@ class UpdateScreenState extends State<UpdateScreen> with SafeSetStateMixin {
9397 if (_betaUpdatesOverride) {
9498 await _checkForBERUpdates (release);
9599 } else {
96- const String url =
97- 'https://api.github.com/repos/thecontrappostoshop /orion/releases/latest' ;
100+ final String url =
101+ 'https://api.github.com/repos/$ _repo /orion/releases/latest' ;
98102 int retryCount = 0 ;
99103 const int maxRetries = 3 ;
100104 const int initialDelay = 750 ;
@@ -169,8 +173,7 @@ class UpdateScreenState extends State<UpdateScreen> with SafeSetStateMixin {
169173 _logger.warning ('release name is empty' );
170174 release = 'BRANCH_dev' ;
171175 }
172- String url =
173- 'https://api.github.com/repos/thecontrappostoshop/orion/releases' ;
176+ String url = 'https://api.github.com/repos/$_repo /orion/releases' ;
174177 int retryCount = 0 ;
175178 const int maxRetries = 3 ;
176179 const int initialDelay = 750 ; // Initial delay in milliseconds
@@ -187,7 +190,7 @@ class UpdateScreenState extends State<UpdateScreen> with SafeSetStateMixin {
187190 final String latestVersion = releaseItem['tag_name' ];
188191 final String commitSha = releaseItem['target_commitish' ];
189192 final commitUrl =
190- 'https://api.github.com/repos/thecontrappostoshop /orion/commits/$commitSha ' ;
193+ 'https://api.github.com/repos/$ _repo /orion/commits/$commitSha ' ;
191194 final commitResponse = await http.get (Uri .parse (commitUrl));
192195 if (commitResponse.statusCode == 200 ) {
193196 final commitJson = json.decode (commitResponse.body);
@@ -325,6 +328,68 @@ class UpdateScreenState extends State<UpdateScreen> with SafeSetStateMixin {
325328 );
326329 }
327330
331+ Future <void > launchUpdateDialog () async {
332+ bool shouldUpdate = await showDialog (
333+ barrierDismissible: false ,
334+ context: context,
335+ builder: (BuildContext context) {
336+ return GlassAlertDialog (
337+ title: Row (
338+ children: [
339+ PhosphorIcon (
340+ PhosphorIcons .download (),
341+ color: Theme .of (context).colorScheme.primary,
342+ size: 32 ,
343+ ),
344+ const SizedBox (width: 12 ),
345+ Text (
346+ 'Update Orion' ,
347+ style: TextStyle (
348+ color: Theme .of (context).colorScheme.primary,
349+ ),
350+ ),
351+ ],
352+ ),
353+ content: const Text (
354+ 'Do you want to update the Orion HMI?\n This will download the latest version from GitHub.' ),
355+ actions: [
356+ GlassButton (
357+ tint: GlassButtonTint .negative,
358+ onPressed: () {
359+ Navigator .of (context).pop (false );
360+ },
361+ style: ElevatedButton .styleFrom (
362+ minimumSize: const Size (0 , 60 ),
363+ ),
364+ child: const Text (
365+ 'Dismiss' ,
366+ style: TextStyle (fontSize: 20 ),
367+ ),
368+ ),
369+ GlassButton (
370+ tint: GlassButtonTint .positive,
371+ onPressed: () {
372+ Navigator .of (context).pop (true );
373+ },
374+ style: ElevatedButton .styleFrom (
375+ minimumSize: const Size (0 , 60 ),
376+ ),
377+ child: const Text (
378+ 'Update Now' ,
379+ style: TextStyle (fontSize: 20 ),
380+ ),
381+ )
382+ ],
383+ );
384+ },
385+ );
386+
387+ if (shouldUpdate) {
388+ final provider = Provider .of <OrionUpdateProvider >(context, listen: false );
389+ await provider.performUpdate (context, _assetUrl);
390+ }
391+ }
392+
328393 @override
329394 Widget build (BuildContext context) {
330395 return Scaffold (
@@ -430,6 +495,7 @@ class UpdateScreenState extends State<UpdateScreen> with SafeSetStateMixin {
430495 children: [
431496 Expanded (
432497 child: GlassButton (
498+ tint: GlassButtonTint .neutral,
433499 onPressed: _viewChangelog,
434500 style: ElevatedButton .styleFrom (
435501 minimumSize: const Size .fromHeight (65 ),
@@ -465,8 +531,9 @@ class UpdateScreenState extends State<UpdateScreen> with SafeSetStateMixin {
465531 width: 12 ), // Add some space between the buttons
466532 Expanded (
467533 child: GlassButton (
534+ tint: GlassButtonTint .positive,
468535 onPressed: () async {
469- _performUpdate (context );
536+ launchUpdateDialog ( );
470537 },
471538 style: ElevatedButton .styleFrom (
472539 minimumSize: const Size .fromHeight (65 ),
@@ -524,7 +591,7 @@ class UpdateScreenState extends State<UpdateScreen> with SafeSetStateMixin {
524591 ),
525592 ),
526593 ),
527- // TODO: Placeholder for Odyssey updater - pending API changes
594+ // TODO: Placeholder for Backend / OS updater - pending API changes
528595 GlassCard (
529596 outlined: true ,
530597 child: Padding (
@@ -539,7 +606,7 @@ class UpdateScreenState extends State<UpdateScreen> with SafeSetStateMixin {
539606 size: 30 ),
540607 const SizedBox (width: 10 ),
541608 const Text (
542- 'Odyssey Updater' ,
609+ 'Backend Updater' ,
543610 style: TextStyle (
544611 fontSize: 26 ,
545612 fontWeight: FontWeight .bold,
@@ -558,196 +625,6 @@ class UpdateScreenState extends State<UpdateScreen> with SafeSetStateMixin {
558625 ),
559626 );
560627 }
561-
562- Future <void > _performUpdate (BuildContext context) async {
563- final String localUser = Platform .environment['USER' ] ?? 'pi' ;
564- final String upgradeFolder = '/home/$localUser /orion_upgrade/' ;
565- final String downloadPath = '$upgradeFolder /orion_armv7.tar.gz' ;
566- final String orionFolder = '/home/$localUser /orion/' ;
567- final String newOrionFolder = '/home/$localUser /orion_new/' ;
568- final String backupFolder = '/home/$localUser /orion_backup/' ;
569- final String scriptPath = '$upgradeFolder /update_orion.sh' ;
570-
571- if (_assetUrl.isEmpty) {
572- _logger.warning ('Asset URL is empty' );
573- return ;
574- }
575-
576- _logger.info ('Downloading from $_assetUrl ' );
577-
578- // Show the update dialog
579- _showUpdateDialog (context, 'Starting update...' );
580-
581- try {
582- // Purge and recreate the upgrade folder
583- final upgradeDir = Directory (upgradeFolder);
584- if (await upgradeDir.exists ()) {
585- try {
586- await upgradeDir.delete (recursive: true );
587- } catch (e) {
588- _logger.warning ('Could not purge upgrade directory' );
589- }
590- }
591- await upgradeDir.create (recursive: true );
592-
593- final newDir = Directory (newOrionFolder);
594- if (await newDir.exists ()) {
595- try {
596- await newDir.delete (recursive: true );
597- } catch (e) {
598- _logger.warning ('Could not purge new Orion directory' );
599- }
600- }
601- await newDir.create (recursive: true );
602-
603- // Update dialog text
604- _updateDialogText (context, 'Downloading update file...' );
605-
606- await Future .delayed (const Duration (seconds: 1 ));
607-
608- // Download the update file
609- final response = await http.get (Uri .parse (_assetUrl));
610- if (response.statusCode == 200 ) {
611- final file = File (downloadPath);
612- await file.writeAsBytes (response.bodyBytes);
613-
614- // Update dialog text
615- _updateDialogText (context, 'Extracting update file...' );
616-
617- await Future .delayed (const Duration (seconds: 1 ));
618-
619- // Extract the update to the new directory
620- final extractResult = await Process .run ('sudo' ,
621- ['tar' , '--overwrite' , '-xzf' , downloadPath, '-C' , newOrionFolder]);
622- if (extractResult.exitCode != 0 ) {
623- _logger.warning (
624- 'Failed to extract update file: ${extractResult .stderr }' );
625- _dismissUpdateDialog (context);
626- return ;
627- }
628-
629- // Create the update script
630- final scriptContent = '''
631- #!/bin/bash
632-
633- # Variables
634- local_user=$localUser
635- orion_folder=$orionFolder
636- new_orion_folder=$newOrionFolder
637- upgrade_folder=$upgradeFolder
638- backup_folder=$backupFolder
639-
640- # If previous backup exists, delete it
641- if [ -d \$ backup_folder ]; then
642- sudo rm -R \$ backup_folder
643- fi
644-
645- # Backup the current Orion directory
646- sudo cp -R \$ orion_folder \$ backup_folder
647-
648- # Remove the old Orion directory
649- sudo rm -R \$ orion_folder
650-
651- # Restore config file
652- sudo cp \$ backup_folder/orion.cfg \$ new_orion_folder
653-
654- # Move the new Orion directory to the original location
655- sudo mv \$ new_orion_folder \$ orion_folder
656-
657- # Delete the upgrade and new folder
658- sudo rm -R \$ upgrade_folder
659-
660- # Fix permissions
661- sudo chown -R \$ local_user:\$ local_user \$ orion_folder
662-
663- # Restart the Orion service
664- sudo systemctl restart orion.service
665- ''' ;
666-
667- final scriptFile = File (scriptPath);
668- await scriptFile.writeAsString (scriptContent);
669- await Process .run ('chmod' , ['+x' , scriptPath]);
670-
671- // Update dialog text
672- _updateDialogText (context, 'Executing update script...' );
673-
674- await Future .delayed (const Duration (seconds: 2 ));
675-
676- // Execute the update script
677- final result = await Process .run ('nohup' , ['sudo' , scriptPath]);
678- if (result.exitCode == 0 ) {
679- _logger.info ('Update script executed successfully' );
680- } else {
681- _logger.warning ('Failed to execute update script: ${result .stderr }' );
682- }
683- } else {
684- _logger.warning ('Failed to download update file' );
685- }
686- } catch (e) {
687- _logger.warning ('Update failed: $e ' );
688- } finally {
689- // Dismiss the update dialog
690- _dismissUpdateDialog (context);
691- }
692- }
693-
694- Future <void > _showUpdateDialog (BuildContext context, String message) {
695- return showDialog (
696- context: context,
697- barrierDismissible: false ,
698- builder: (BuildContext context) {
699- return SafeArea (
700- child: Dialog (
701- backgroundColor: Colors .transparent,
702- insetPadding: EdgeInsets .zero,
703- child: Container (
704- width: MediaQuery .of (context).size.width,
705- height: MediaQuery .of (context).size.height,
706- color: Theme .of (context).colorScheme.surface,
707- child: Column (
708- mainAxisSize: MainAxisSize .min,
709- mainAxisAlignment: MainAxisAlignment .center,
710- children: < Widget > [
711- const SizedBox (
712- height: 75 ,
713- width: 75 ,
714- child: CircularProgressIndicator (
715- strokeWidth: 6 ,
716- ),
717- ),
718- const SizedBox (height: 60 ),
719- Text (
720- message,
721- textAlign: TextAlign .center,
722- style: const TextStyle (fontSize: 32 ),
723- ),
724- ],
725- ),
726- ),
727- ),
728- );
729- },
730- );
731- }
732-
733- void _updateDialogText (BuildContext context, String message) {
734- if (Navigator .of (context).canPop ()) {
735- // Show the new dialog first
736- _showUpdateDialog (context, message).then ((_) {
737- // Pop the old dialog after the new one has been rendered
738- if (Navigator .of (context).canPop ()) {
739- Navigator .of (context).pop ();
740- }
741- });
742- } else {
743- // If there's no dialog to pop, just show the new one
744- _showUpdateDialog (context, message);
745- }
746- }
747-
748- void _dismissUpdateDialog (BuildContext context) {
749- if (Navigator .of (context).canPop ()) {
750- Navigator .of (context).pop ();
751- }
752- }
753628}
629+ // Update flow is handled by OrionUpdateProvider via
630+ // `Provider.of<OrionUpdateProvider>(context, listen: false).performUpdate(...)`.
0 commit comments