app_update_dialog.dart 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import 'package:flutter/material.dart';
  2. import 'package:logging/logging.dart';
  3. import 'package:open_file/open_file.dart';
  4. import 'package:photos/core/configuration.dart';
  5. import 'package:photos/core/network.dart';
  6. import 'package:photos/services/update_service.dart';
  7. class AppUpdateDialog extends StatefulWidget {
  8. final LatestVersionInfo latestVersionInfo;
  9. AppUpdateDialog(this.latestVersionInfo, {Key key}) : super(key: key);
  10. @override
  11. _AppUpdateDialogState createState() => _AppUpdateDialogState();
  12. }
  13. class _AppUpdateDialogState extends State<AppUpdateDialog> {
  14. @override
  15. Widget build(BuildContext context) {
  16. final List<Widget> changelog = [];
  17. for (final log in widget.latestVersionInfo.changelog) {
  18. changelog.add(
  19. Padding(
  20. padding: const EdgeInsets.fromLTRB(8, 4, 0, 4),
  21. child: Text("- " + log, style: Theme.of(context).textTheme.caption),
  22. ),
  23. );
  24. }
  25. final content = Column(
  26. crossAxisAlignment: CrossAxisAlignment.start,
  27. mainAxisSize: MainAxisSize.min,
  28. children: [
  29. Text(
  30. widget.latestVersionInfo.name,
  31. style: TextStyle(
  32. fontSize: 20,
  33. fontWeight: FontWeight.bold,
  34. ),
  35. ),
  36. Padding(padding: EdgeInsets.all(8)),
  37. Text(
  38. "Changelog",
  39. style: TextStyle(
  40. fontSize: 18,
  41. ),
  42. ),
  43. Padding(padding: EdgeInsets.all(4)),
  44. Column(
  45. crossAxisAlignment: CrossAxisAlignment.start,
  46. children: changelog,
  47. ),
  48. Padding(padding: EdgeInsets.all(8)),
  49. SizedBox(
  50. width: double.infinity,
  51. height: 64,
  52. child: OutlinedButton(
  53. child: Text(
  54. "Update",
  55. ),
  56. style: Theme.of(context).outlinedButtonTheme.style.copyWith(
  57. textStyle: MaterialStateProperty.resolveWith<TextStyle>(
  58. (Set<MaterialState> states) {
  59. return Theme.of(context).textTheme.subtitle1;
  60. },
  61. ),
  62. ),
  63. onPressed: () async {
  64. Navigator.pop(context);
  65. showDialog(
  66. context: context,
  67. builder: (BuildContext context) {
  68. return ApkDownloaderDialog(widget.latestVersionInfo);
  69. },
  70. barrierDismissible: false,
  71. );
  72. },
  73. ),
  74. ),
  75. ],
  76. );
  77. final shouldForceUpdate = UpdateService.instance.shouldForceUpdate(widget.latestVersionInfo);
  78. return WillPopScope(
  79. onWillPop: () async => !shouldForceUpdate,
  80. child: AlertDialog(
  81. title: Text(
  82. shouldForceUpdate ? "Critical update available" : "Update available",
  83. ),
  84. content: content,
  85. ),
  86. );
  87. }
  88. }
  89. class ApkDownloaderDialog extends StatefulWidget {
  90. final LatestVersionInfo versionInfo;
  91. ApkDownloaderDialog(this.versionInfo, {Key key}) : super(key: key);
  92. @override
  93. _ApkDownloaderDialogState createState() => _ApkDownloaderDialogState();
  94. }
  95. class _ApkDownloaderDialogState extends State<ApkDownloaderDialog> {
  96. String _saveUrl;
  97. double _downloadProgress;
  98. @override
  99. void initState() {
  100. super.initState();
  101. _saveUrl =
  102. Configuration.instance.getTempDirectory() + "ente-" + widget.versionInfo.name + ".apk";
  103. _downloadApk();
  104. }
  105. @override
  106. Widget build(BuildContext context) {
  107. return WillPopScope(
  108. onWillPop: () async => false,
  109. child: AlertDialog(
  110. title: Text(
  111. "Downloading...",
  112. style: TextStyle(
  113. fontSize: 16,
  114. ),
  115. textAlign: TextAlign.center,
  116. ),
  117. content: LinearProgressIndicator(
  118. value: _downloadProgress,
  119. valueColor: AlwaysStoppedAnimation<Color>(Theme.of(context).buttonColor),
  120. ),
  121. ),
  122. );
  123. }
  124. Future<void> _downloadApk() async {
  125. try {
  126. await Network.instance.getDio().download(
  127. widget.versionInfo.url,
  128. _saveUrl,
  129. onReceiveProgress: (count, _) {
  130. setState(() {
  131. _downloadProgress = count / widget.versionInfo.size;
  132. });
  133. },
  134. );
  135. Navigator.of(context, rootNavigator: true).pop('dialog');
  136. OpenFile.open(_saveUrl);
  137. } catch (e) {
  138. Logger("ApkDownloader").severe(e);
  139. AlertDialog alert = AlertDialog(
  140. title: Text("Sorry"),
  141. content: Text("The download could not be completed"),
  142. actions: [
  143. TextButton(
  144. child: Text(
  145. "Ignore",
  146. style: TextStyle(
  147. color: Colors.white,
  148. ),
  149. ),
  150. onPressed: () {
  151. Navigator.of(context, rootNavigator: true).pop('dialog');
  152. Navigator.of(context, rootNavigator: true).pop('dialog');
  153. },
  154. ),
  155. TextButton(
  156. child: Text(
  157. "Retry",
  158. style: TextStyle(
  159. color: Theme.of(context).buttonColor,
  160. ),
  161. ),
  162. onPressed: () {
  163. Navigator.of(context, rootNavigator: true).pop('dialog');
  164. Navigator.of(context, rootNavigator: true).pop('dialog');
  165. showDialog(
  166. context: context,
  167. builder: (BuildContext context) {
  168. return ApkDownloaderDialog(widget.versionInfo);
  169. },
  170. barrierDismissible: false,
  171. );
  172. },
  173. ),
  174. ],
  175. );
  176. showDialog(
  177. context: context,
  178. builder: (BuildContext context) {
  179. return alert;
  180. },
  181. barrierColor: Colors.black87,
  182. );
  183. return;
  184. }
  185. }
  186. }