Skip to content

Commit

Permalink
feat: Logs service (+ email attachment) (openfoodfacts#2303)
Browse files Browse the repository at this point in the history
* Logs service (+ email attachment)

* Fix flutter format warning

* Reformat code

* Remove fimber_io dependency to have our own implementations instead

* Add close:true to email dialog

* Add SENDTO query to AndroidManifest

* Remove useless comment
  • Loading branch information
g123k authored Jun 23, 2022
1 parent 35295c6 commit 97d36bc
Show file tree
Hide file tree
Showing 23 changed files with 580 additions and 39 deletions.
4 changes: 4 additions & 0 deletions packages/smooth_app/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
<action android:name="android.intent.action.SEND"/>
<data android:mimeType="*/*"/>
</intent>
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto" />
</intent>
</queries>

<application
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:smooth_app/services/smooth_services.dart';

/// Asset cache helper class
class AssetCacheHelper {
Expand All @@ -25,7 +26,7 @@ class AssetCacheHelper {
height: height ?? width,
);

void notFound() => debugPrint(
void notFound() => Logs.d(
'please download $url and put it in asset somewhere like $cachedFilenames');

Exception loadException() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:smooth_app/database/dao_product.dart';
import 'package:smooth_app/database/dao_product_list.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/helpers/analytics_helper.dart';
import 'package:smooth_app/services/smooth_services.dart';

enum ScannedProductState {
FOUND,
Expand Down Expand Up @@ -57,7 +58,7 @@ class ContinuousScanModel with ChangeNotifier {
}
return this;
} catch (e) {
debugPrint('exception: $e');
Logs.e('Load database error', ex: e);
}
return null;
}
Expand All @@ -78,7 +79,7 @@ class ContinuousScanModel with ChangeNotifier {
}
return true;
} catch (e) {
debugPrint('exception: $e');
Logs.e('Refresh database error', ex: e);
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:openfoodfacts/utils/OpenFoodAPIConfiguration.dart';
import 'package:smooth_app/database/dao_secured_string.dart';
import 'package:smooth_app/services/smooth_services.dart';

class UserManagementProvider with ChangeNotifier {
static const String _USER_ID = 'user_id';
Expand Down Expand Up @@ -48,7 +49,7 @@ class UserManagementProvider with ChangeNotifier {
/// manually overwritten from an external apk.
DaoSecuredString.remove(key: _USER_ID);
DaoSecuredString.remove(key: _PASSWORD);
debugPrint('Credentials query failed, you have been logged out');
Logs.e('Credentials query failed, you have been logged out');
}

if (userId == null || password == null) {
Expand Down
6 changes: 3 additions & 3 deletions packages/smooth_app/lib/database/dao_product_list.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:openfoodfacts/model/Product.dart';
import 'package:smooth_app/data_models/product_list.dart';
import 'package:smooth_app/database/abstract_dao.dart';
import 'package:smooth_app/database/dao_product.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/services/smooth_services.dart';

/// "Total size" fake value for lists that are not partial/paged.
const int _uselessTotalSizeValue = 0;
Expand Down Expand Up @@ -160,10 +160,10 @@ class DaoProductList extends AbstractDao {
barcodes.add(barcode);
products[barcode] = product;
} else {
debugPrint('unexpected: unknown product for $barcode');
Logs.e('unexpected: unknown product for $barcode');
}
} catch (e) {
debugPrint('unexpected: exception for product $barcode');
Logs.e('unexpected: unknown product for $barcode', ex: e);
}
}
productList.set(barcodes, products);
Expand Down
2 changes: 2 additions & 0 deletions packages/smooth_app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@
},
"support_join_slack": "Ask for help in our Slack channel",
"support_via_email": "Send us an e-mail",
"support_via_email_include_logs_dialog_title": "Send app logs?",
"support_via_email_include_logs_dialog_body": "Do you wish to include application logs in attachment to your email?",
"termsOfUse": "Terms of use",
"@termsOfUse": {},
"about_this_app": "About this app",
Expand Down
2 changes: 2 additions & 0 deletions packages/smooth_app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import 'package:smooth_app/helpers/camera_helper.dart';
import 'package:smooth_app/helpers/data_importer/smooth_app_data_importer.dart';
import 'package:smooth_app/helpers/network_config.dart';
import 'package:smooth_app/pages/onboarding/onboarding_flow_navigator.dart';
import 'package:smooth_app/services/smooth_services.dart';
import 'package:smooth_app/themes/smooth_theme.dart';
import 'package:smooth_app/themes/theme_provider.dart';

Expand Down Expand Up @@ -89,6 +90,7 @@ Future<bool> _init1() async {
return false;
}

await SmoothServices().init();
await setupAppNetworkConfig();
await UserManagementProvider.mountCredentials();
_userPreferences = await UserPreferences.getUserPreferences();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_email_sender/flutter_email_sender.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:mailto/mailto.dart';
import 'package:openfoodfacts/utils/OpenFoodAPIConfiguration.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/data_models/user_management_provider.dart';
Expand All @@ -20,7 +20,6 @@ import 'package:smooth_app/pages/preferences/user_preferences_page.dart';
import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart';
import 'package:smooth_app/pages/product/common/product_query_page_helper.dart';
import 'package:smooth_app/pages/user_management/login_page.dart';
import 'package:url_launcher/url_launcher.dart';

class UserPreferencesAccount extends AbstractUserPreferences {
UserPreferencesAccount({
Expand Down Expand Up @@ -295,12 +294,13 @@ class _UserPreferencesPageState extends State<UserPreferencesSection> {
const UserPreferencesListItemDivider(),
ListTile(
onTap: () async {
final Mailto mailtoLink = Mailto(
to: <String>['contact@openfoodfacts.org'],
subject: appLocalizations.email_subject_account_deletion,
final Email email = Email(
body: appLocalizations.email_body_account_deletion(userId),
subject: appLocalizations.email_subject_account_deletion,
recipients: <String>['contact@openfoodfacts.org'],
);
await launchUrl(Uri.parse('$mailtoLink'));

await FlutterEmailSender.send(email);
},
title: Text(appLocalizations.account_delete),
leading: const Icon(Icons.delete),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_email_sender/flutter_email_sender.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:mailto/mailto.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:smooth_app/data_models/user_preferences.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart';
import 'package:smooth_app/helpers/launch_url_helper.dart';
import 'package:smooth_app/pages/preferences/abstract_user_preferences.dart';
import 'package:smooth_app/pages/preferences/user_preferences_list_tile.dart';
import 'package:smooth_app/pages/preferences/user_preferences_page.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:smooth_app/services/smooth_services.dart';

/// Display of "Connect" for the preferences page.
class UserPreferencesConnect extends AbstractUserPreferences {
Expand Down Expand Up @@ -79,13 +80,34 @@ class UserPreferencesConnect extends AbstractUserPreferences {
title: appLocalizations.support_via_email,
leading: UserPreferencesListTile.getTintedIcon(Icons.drafts, context),
onTap: () async {
final Mailto mailtoLink = Mailto(
to: <String>['contact@openfoodfacts.org'],
// This shouldn't be translated as its a debug message to OpenFoodFacts
subject: 'Smoothie help',
final bool? includeLogs = await showDialog<bool>(
context: context,
builder: (BuildContext context) {
return SmoothAlertDialog(
title: appLocalizations
.support_via_email_include_logs_dialog_title,
body: Text(
appLocalizations
.support_via_email_include_logs_dialog_body,
),
close: true,
positiveAction: SmoothActionButton(
text: appLocalizations.yes,
onPressed: () => Navigator.of(context).pop(true)),
negativeAction: SmoothActionButton(
text: appLocalizations.no,
onPressed: () => Navigator.of(context).pop(false)),
);
});

final Email email = Email(
body: await _emailBody,
subject: 'Smoothie help',
recipients: <String>['contact@openfoodfacts.org'],
attachmentPaths: includeLogs == true ? Logs.logFilesPaths : null,
);
await launchUrl(Uri.parse('$mailtoLink'));

await FlutterEmailSender.send(email);
},
),
];
Expand Down
8 changes: 6 additions & 2 deletions packages/smooth_app/lib/pages/scan/camera_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:camera_platform_interface/camera_platform_interface.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:smooth_app/data_models/user_preferences.dart';
import 'package:smooth_app/services/smooth_services.dart';

/// A lifecycle-aware [CameraController]
/// On Android it supports pause/resume feed
Expand Down Expand Up @@ -68,9 +69,12 @@ class SmoothCameraController extends CameraController {
.onCameraClosing(cameraId)
.listen((CameraClosingEvent event) async {
value = value.markAsClosed();
Logs.d('Camera closed!');
});

_updateState(_CameraState.resumed);
} else {
Logs.w('Controller already initialized!');
}
}

Expand Down Expand Up @@ -124,7 +128,7 @@ class SmoothCameraController extends CameraController {
// The pause process can sometimes be too long, in that case, we just for
// it to be finished
_hasAPendingResume = true;
debugPrint('Preview not paused, will be restarted later…');
Logs.d('Preview not paused, will be restarted later…');
return;
} else if (_state == _CameraState.paused) {
return resumePreview();
Expand Down Expand Up @@ -244,7 +248,7 @@ class SmoothCameraController extends CameraController {
void _updateState(_CameraState newState) {
if (newState != _state) {
_state = newState;
debugPrint('New camera state = $_state');
Logs.d('New camera state = $_state');

// Notify the UI to ensure a setState is called
if (_state == _CameraState.resumed) {
Expand Down
16 changes: 7 additions & 9 deletions packages/smooth_app/lib/pages/scan/ml_kit_scan_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'dart:math' as math;

import 'package:audioplayers/audioplayers.dart';
import 'package:camera/camera.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
Expand All @@ -20,6 +19,7 @@ import 'package:smooth_app/pages/scan/camera_controller.dart';
import 'package:smooth_app/pages/scan/lifecycle_manager.dart';
import 'package:smooth_app/pages/scan/mkit_scan_helper.dart';
import 'package:smooth_app/pages/scan/scan_visor.dart';
import 'package:smooth_app/services/smooth_services.dart';
import 'package:smooth_app/widgets/lifecycle_aware_widget.dart';
import 'package:smooth_app/widgets/screen_visibility.dart';

Expand Down Expand Up @@ -298,14 +298,10 @@ class MLKitScannerPageState extends LifecycleAwareState<MLKitScannerPage>
});
}
} on CameraException catch (e) {
if (kDebugMode) {
// TODO(M123): Show error message
debugPrint(e.toString());
}
// TODO(M123): Show error message
Logs.d('On camera error', ex: e);
} on FlutterError catch (e) {
if (kDebugMode) {
debugPrint(e.toString());
}
Logs.d('On camera (Flutter part) error', ex: e);
}

_redrawScreen();
Expand Down Expand Up @@ -338,7 +334,9 @@ class MLKitScannerPageState extends LifecycleAwareState<MLKitScannerPage>
_stopImageStream();
} else {
// TODO(M123): Handle errors better
debugPrint(_controller!.value.errorDescription);
Logs.e(
'On camera controller error : ${_controller!.value.errorDescription}',
);
}
}
}
Expand Down
23 changes: 23 additions & 0 deletions packages/smooth_app/lib/services/logs/fimber/fimber_helper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'package:smooth_app/services/logs/smooth_log_levels.dart';

extension LogLevelExtension on LogLevel {
String get fimberLevel {
switch (this) {
case LogLevel.verbose:
return 'V';
case LogLevel.debug:
return 'D';
case LogLevel.info:
return 'I';
case LogLevel.warning:
return 'W';
case LogLevel.error:
return 'E';
}
}
}

extension LogLevelsExtension on Iterable<LogLevel> {
List<String> get fimberLevels =>
map((LogLevel level) => level.fimberLevel).toList(growable: false);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:fimber/fimber.dart';
import 'package:smooth_app/services/logs/fimber/fimber_helper.dart';
import 'package:smooth_app/services/logs/smooth_log_levels.dart';

abstract class BaseFimberTree extends LogTree {
BaseFimberTree({required List<LogLevel> logLevels})
: assert(logLevels.isNotEmpty),
_logLevels = logLevels
.map((LogLevel level) => level.fimberLevel)
.toList(growable: false),
super();

final List<String> _logLevels;

@override
List<String> getLevels() => _logLevels;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:fimber/fimber.dart';
import 'package:smooth_app/services/logs/fimber/fimber_helper.dart';
import 'package:smooth_app/services/logs/smooth_log_levels.dart';

class DebugFimberTree extends DebugTree {
DebugFimberTree({required List<LogLevel> logLevels})
: assert(logLevels.isNotEmpty),
super(
logLevels: logLevels
.map((LogLevel level) => level.fimberLevel)
.toList(growable: false),
);
}
Loading

0 comments on commit 97d36bc

Please sign in to comment.