Separate Exception Handler class from main [skip ci]

OmarHatem committed Feb 1, 2023 at 17:37 UTC 15e7395fe9084b7d15a3501246f85f1c60cd2596
2 files changed +108 -104
lib/main.dart
+4 -104
@@ -1,22 +1,12 @@
1 import 'dart:async';
2 -import 'dart:convert';
3 -import 'dart:io';
4 -import 'dart:isolate';
5 -import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 import 'package:cake_wallet/core/auth_service.dart';
3 import 'package:cake_wallet/entities/language_service.dart';
4 import 'package:cake_wallet/buy/order.dart';
9 -import 'package:cake_wallet/entities/preferences_key.dart';
10 -import 'package:cake_wallet/ionia/ionia_category.dart';
11 -import 'package:cake_wallet/ionia/ionia_merchant.dart';
12 -import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
5 import 'package:cake_wallet/store/yat/yat_store.dart';
14 -import 'package:cake_wallet/themes/theme_list.dart';
15 -import 'package:cake_wallet/utils/show_pop_up.dart';
6 +import 'package:cake_wallet/utils/exception_handler.dart';
7 import 'package:flutter/foundation.dart';
8 import 'package:flutter/material.dart';
9 import 'package:flutter/services.dart';
19 -import 'package:flutter_mailer/flutter_mailer.dart';
10 import 'package:hive/hive.dart';
11 import 'package:cake_wallet/di.dart';
12 import 'package:path_provider/path_provider.dart';
@@ -50,21 +40,18 @@ import 'package:cake_wallet/wallet_type_utils.dart';
40 final navigatorKey = GlobalKey<NavigatorState>();
41 final rootKey = GlobalKey<RootState>();
42 final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
53 -bool hasError = false;
43
44 Future<void> main() async {
45
46 await runZonedGuarded(() async {
47 WidgetsFlutterBinding.ensureInitialized();
48
60 - FlutterError.onError = (errorDetails) {
61 - _onError(errorDetails);
62 - };
49 + FlutterError.onError = ExceptionHandler.onError;
50
51 /// A callback that is invoked when an unhandled error occurs in the root
52 /// isolate.
53 PlatformDispatcher.instance.onError = (error, stack) {
67 - _onError(FlutterErrorDetails(exception: error, stack: stack));
54 + ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack));
55
56 return true;
57 };
@@ -155,97 +142,10 @@ Future<void> main() async {
142 initialMigrationVersion: 19);
143 runApp(App());
144 }, (error, stackTrace) async {
158 - _onError(FlutterErrorDetails(exception: error, stack: stackTrace));
145 + ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stackTrace));
146 });
147 }
148
162 -void _saveException(String? error, StackTrace? stackTrace) async {
163 - final appDocDir = await getApplicationDocumentsDirectory();
164 -
165 - final file = File('${appDocDir.path}/error.txt');
166 - final exception = {
167 - "${DateTime.now()}": {
168 - "Error": error,
169 - "StackTrace": stackTrace.toString(),
170 - }
171 - };
172 -
173 - const String separator =
174 - '''\n\n==========================================================
175 - ==========================================================\n\n''';
176 -
177 - await file.writeAsString(
178 - jsonEncode(exception) + separator,
179 - mode: FileMode.append,
180 - );
181 -}
182 -
183 -void _sendExceptionFile() async {
184 - try {
185 - final appDocDir = await getApplicationDocumentsDirectory();
186 -
187 - final file = File('${appDocDir.path}/error.txt');
188 -
189 - final MailOptions mailOptions = MailOptions(
190 - subject: 'Mobile App Issue',
191 - recipients: ['support@cakewallet.com'],
192 - attachments: [file.path],
193 - );
194 -
195 - final result = await FlutterMailer.send(mailOptions);
196 -
197 - // Clear file content if the error was sent or saved.
198 - // On android we can't know if it was sent or saved
199 - if (result.name == MailerResponse.sent.name ||
200 - result.name == MailerResponse.saved.name ||
201 - result.name == MailerResponse.android.name) {
202 - file.writeAsString("", mode: FileMode.write);
203 - }
204 - } catch (e, s) {
205 - _saveException(e.toString(), s);
206 - }
207 -}
208 -
209 -void _onError(FlutterErrorDetails errorDetails) {
210 - // Add Exception for user's network connection issues to not be reported
211 - if (errorDetails.exception.toString().contains("Software caused connection abort")) {
212 - return;
213 - }
214 -
215 - _saveException(errorDetails.exception.toString(), errorDetails.stack);
216 -
217 - if (hasError) {
218 - return;
219 - }
220 - hasError = true;
221 -
222 - WidgetsBinding.instance.addPostFrameCallback(
223 - (timeStamp) async {
224 - await showPopUp<void>(
225 - context: navigatorKey.currentContext!,
226 - builder: (context) {
227 - return AlertWithTwoActions(
228 - isDividerExist: true,
229 - alertTitle: S.of(context).error,
230 - alertContent: S.of(context).error_dialog_content,
231 - rightButtonText: S.of(context).send,
232 - leftButtonText: S.of(context).do_not_send,
233 - actionRightButton: () {
234 - Navigator.of(context).pop();
235 - _sendExceptionFile();
236 - },
237 - actionLeftButton: () {
238 - Navigator.of(context).pop();
239 - },
240 - );
241 - },
242 - );
243 -
244 - hasError = false;
245 - },
246 - );
247 -}
248 -
149 Future<void> initialSetup(
150 {required SharedPreferences sharedPreferences,
151 required Box<Node> nodes,
lib/utils/exception_handler.dart new
+104
@@ -0,0 +1,104 @@
1 +import 'dart:convert';
2 +import 'dart:io';
3 +
4 +import 'package:cake_wallet/generated/i18n.dart';
5 +import 'package:cake_wallet/main.dart';
6 +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
7 +import 'package:cake_wallet/utils/show_pop_up.dart';
8 +import 'package:flutter/material.dart';
9 +import 'package:flutter_mailer/flutter_mailer.dart';
10 +import 'package:path_provider/path_provider.dart';
11 +
12 +class ExceptionHandler {
13 + static bool _hasError = false;
14 +
15 + static void _saveException(String? error, StackTrace? stackTrace) async {
16 + final appDocDir = await getApplicationDocumentsDirectory();
17 +
18 + final file = File('${appDocDir.path}/error.txt');
19 + final exception = {
20 + "${DateTime.now()}": {
21 + "Error": error,
22 + "StackTrace": stackTrace.toString(),
23 + }
24 + };
25 +
26 + const String separator = '''\n\n==========================================================
27 + ==========================================================\n\n''';
28 +
29 + await file.writeAsString(
30 + jsonEncode(exception) + separator,
31 + mode: FileMode.append,
32 + );
33 + }
34 +
35 + static void _sendExceptionFile() async {
36 + try {
37 + final appDocDir = await getApplicationDocumentsDirectory();
38 +
39 + final file = File('${appDocDir.path}/error.txt');
40 +
41 + final MailOptions mailOptions = MailOptions(
42 + subject: 'Mobile App Issue',
43 + recipients: ['support@cakewallet.com'],
44 + attachments: [file.path],
45 + );
46 +
47 + final result = await FlutterMailer.send(mailOptions);
48 +
49 + // Clear file content if the error was sent or saved.
50 + // On android we can't know if it was sent or saved
51 + if (result.name == MailerResponse.sent.name ||
52 + result.name == MailerResponse.saved.name ||
53 + result.name == MailerResponse.android.name) {
54 + file.writeAsString("", mode: FileMode.write);
55 + }
56 + } catch (e, s) {
57 + _saveException(e.toString(), s);
58 + }
59 + }
60 +
61 + static void onError(FlutterErrorDetails errorDetails) {
62 + if (_isErrorFromUser(errorDetails.exception.toString())) {
63 + return;
64 + }
65 +
66 + _saveException(errorDetails.exception.toString(), errorDetails.stack);
67 +
68 + if (_hasError) {
69 + return;
70 + }
71 + _hasError = true;
72 +
73 + WidgetsBinding.instance.addPostFrameCallback(
74 + (timeStamp) async {
75 + await showPopUp<void>(
76 + context: navigatorKey.currentContext!,
77 + builder: (context) {
78 + return AlertWithTwoActions(
79 + isDividerExist: true,
80 + alertTitle: S.of(context).error,
81 + alertContent: S.of(context).error_dialog_content,
82 + rightButtonText: S.of(context).send,
83 + leftButtonText: S.of(context).do_not_send,
84 + actionRightButton: () {
85 + Navigator.of(context).pop();
86 + _sendExceptionFile();
87 + },
88 + actionLeftButton: () {
89 + Navigator.of(context).pop();
90 + },
91 + );
92 + },
93 + );
94 +
95 + _hasError = false;
96 + },
97 + );
98 + }
99 +
100 + /// User related errors to be added as exceptions here to not report
101 + static bool _isErrorFromUser(String error) {
102 + return error.contains("Software caused connection abort"); // User connection issue
103 + }
104 +}