Skip to content

Commit

Permalink
feat: Allow to change the camera mode without restarting (#3008)
Browse files Browse the repository at this point in the history
* Allow to change the camera mode (aka alternative mode) without restarting the app

* Fix test errors

* Update pubspec.lock

* Fix goldens

* Update goldens
  • Loading branch information
g123k authored Sep 14, 2022
1 parent 706247f commit acb5fac
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 103 deletions.
11 changes: 9 additions & 2 deletions packages/smooth_app/lib/data_models/user_preferences.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@ class UserPreferences extends ChangeNotifier {
UserPreferences._shared(final SharedPreferences sharedPreferences)
: _sharedPreferences = sharedPreferences;

/// Singleton
static UserPreferences? _instance;
final SharedPreferences _sharedPreferences;

static Future<UserPreferences> getUserPreferences() async {
final SharedPreferences preferences = await SharedPreferences.getInstance();
return UserPreferences._shared(preferences);
if (_instance == null) {
final SharedPreferences preferences =
await SharedPreferences.getInstance();
_instance = UserPreferences._shared(preferences);
}

return _instance!;
}

static const String _TAG_PREFIX_IMPORTANCE = 'IMPORTANCE_AS_STRING';
Expand Down
2 changes: 1 addition & 1 deletion packages/smooth_app/lib/helpers/provider_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class _ListenerState<T> extends SingleChildState<Listener<T>> {
@override
Widget buildWithChild(BuildContext context, Widget? child) {
final T? oldValue = _oldValue;
final T newValue = Provider.of<T>(context);
final T newValue = context.watch<T>();
_oldValue = newValue;

widget.listener(
Expand Down
40 changes: 1 addition & 39 deletions packages/smooth_app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -914,36 +914,6 @@
"@camera_settings_title": {
"description": "Name of the camera section in the settings"
},
"camera_high_resolution_preset_toggle_title": "High performance mode",
"@camera_high_resolution_preset_toggle_title": {
"description": "Title for the Camera high resolution toggle"
},
"camera_high_resolution_preset_toggle_subtitle": "When enabled, barcodes decoding quality should be better, but may impact performance and battery life. Requires an application restart.",
"@camera_high_resolution_preset_toggle_subtitle": {
"description": "SubTitle for the Camera high resolution toggle"
},
"camera_focus_point_algorithm_title": "Focus mode",
"@camera_focus_point_algorithm_title": {
"description": "Title for the Camera focus mode (Android only)"
},
"camera_focus_point_algorithm_subtitle": "Current mode: {mode}\n{reason}",
"@camera_focus_point_algorithm_subtitle": {
"description": "SubTitle for the Camera focus mode (Android only)",
"placeholders": {
"mode": {
"type": "String"
},
"reason": {
"type": "String"
}
}
},
"camera_focus_point_algorithm_value_auto_label": "Auto",
"camera_focus_point_algorithm_value_auto_description": "Let the system choose automatically between fast and safe modes.",
"camera_focus_point_algorithm_value_new_algorithm_label": "Fast mode",
"camera_focus_point_algorithm_value_new_algorithm_description": "A mode recommended to recent devices.",
"camera_focus_point_algorithm_value_old_algorithm_label": "Safe mode",
"camera_focus_point_algorithm_value_old_algorithm_description": "If fast mode doesn't work, this should resolve your focus issues.",
"camera_play_sound_title": "Play a sound on scan",
"@camera_play_sound_title": {
"description": "Title for the Camera play sound toggle"
Expand All @@ -956,7 +926,7 @@
"@camera_alternative_mode_title": {
"description": "Title for the Alternative mode for the camera/scanner"
},
"camera_alternative_mode_subtitle": "If your device is unable to scan barcodes, please try this mode. If it works well, please communicate us your device name: {device}, in order to make this mode the default one for your device.",
"camera_alternative_mode_subtitle": "If your device is unable to scan barcodes, please try this mode.\n\nIf it works well, please communicate us your device name: {device}, in order to make this mode the default one for your device.",
"@camera_alternative_mode_subtitle": {
"description": "SubTitle for the Alternative mode for the camera/scanner",
"placeholders": {
Expand All @@ -969,14 +939,6 @@
"@camera_alternative_mode_confirm_dialog_title": {
"description": "Title for the dialog explaining the alternative mode has changed"
},
"camera_alternative_mode_confirm_dialog_body": "The new mode will only be used after stopping, then relaunching the app!",
"@camera_alternative_mode_confirm_dialog_body": {
"description": "Content for the dialog explaining the alternative mode has changed = restart the app"
},
"camera_alternative_mode_confirm_dialog_button": "Duly noted!",
"@camera_alternative_mode_confirm_dialog_button": {
"description": "Button for the dialog to confirm the user has understood"
},
"app_haptic_feedback_title": "Vibration & Haptics",
"@app_haptic_feedback_title": {
"description": "Title for the Haptic feedback toggle"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import 'package:matomo_tracker/matomo_tracker.dart';
import 'package:provider/provider.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/generic_lib/widgets/language_selector.dart';
import 'package:smooth_app/helpers/analytics_helper.dart';
import 'package:smooth_app/helpers/camera_helper.dart';
Expand Down Expand Up @@ -289,34 +288,12 @@ class _CameraAlternativeModeSetting extends StatelessWidget {
.camera_alternative_mode_subtitle(snapshot.data![1] as String),
value: enabled,
onChanged: (final bool value) {
_showWarningAfterChange(context);
userPreferences.setUseAlternativeCameraMode(value);
},
);
},
);
}

void _showWarningAfterChange(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);

showDialog<void>(
context: context,
builder: (BuildContext subContext) {
return SmoothAlertDialog(
title:
appLocalizations.camera_alternative_mode_confirm_dialog_title,
body: Text(
appLocalizations.camera_alternative_mode_confirm_dialog_body,
),
positiveAction: SmoothActionButton(
text: appLocalizations
.camera_alternative_mode_confirm_dialog_button,
onPressed: () => Navigator.pop(subContext),
),
);
});
}
}

class _ProductsSettings extends StatelessWidget {
Expand Down
35 changes: 30 additions & 5 deletions packages/smooth_app/lib/pages/scan/camera_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,14 @@ class SmoothCameraController extends CameraController {
/// Listen to camera error events
StreamSubscription<CameraErrorEvent>? _errorListener;

// Last focus point position
/// Last focus point position
Offset? _focusPoint;

/// Does the camera use the alternative mode (file based implementation)?
/// Enabled to [false] by default, as [changeImageMode] can be called, even if
/// [init] wasn't called before.
bool _persistToFileMode = false;

Future<void> init({
required FocusMode focusMode,
required Offset focusPoint,
Expand Down Expand Up @@ -93,16 +98,36 @@ class SmoothCameraController extends CameraController {

@protected
Future<void> startStream(onLatestImageAvailable onAvailable) async {
final bool useAlternativeCameraMode =
preferences.useAlternativeCameraMode ??
await AlternativeCameraMode.isAWhitelistedDevice;
_persistToFileMode = preferences.useAlternativeCameraMode ??
await AlternativeCameraMode.isAWhitelistedDevice;

return startImageStream(
onAvailable,
persistToFile: useAlternativeCameraMode,
persistToFile: _persistToFileMode,
);
}

Future<void> reloadImageMode() async {
final bool? alternativeCameraMode = preferences.useAlternativeCameraMode;

/// Keep using the default value
if (alternativeCameraMode == null) {
return;
}

if (alternativeCameraMode != _persistToFileMode) {
_persistToFileMode = alternativeCameraMode;
await changeImageMode(_persistToFileMode);
}
}

/// Never use this method directly, use [reloadImageMode] instead
@protected
@override
Future<bool> changeImageMode(bool persistToFile) {
return super.changeImageMode(persistToFile);
}

/// Never use this method directly, by through [startStream]
/// [persistToFile] is what we call the "alternative" mode
@protected
Expand Down
43 changes: 25 additions & 18 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,7 @@ import 'dart:math' as math;

import 'package:audioplayers/audioplayers.dart';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter/material.dart' hide Listener;
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:matomo_tracker/matomo_tracker.dart';
Expand All @@ -14,6 +14,7 @@ import 'package:smooth_app/data_models/user_preferences.dart';
import 'package:smooth_app/helpers/camera_helper.dart';
import 'package:smooth_app/helpers/collections_helper.dart';
import 'package:smooth_app/helpers/haptic_feedback_helper.dart';
import 'package:smooth_app/helpers/provider_helper.dart';
import 'package:smooth_app/pages/page_manager.dart';
import 'package:smooth_app/pages/preferences/user_preferences_dev_mode.dart';
import 'package:smooth_app/pages/scan/camera_controller.dart';
Expand Down Expand Up @@ -137,24 +138,30 @@ class MLKitScannerPageState extends LifecycleAwareState<MLKitScannerPage>

@override
Widget build(BuildContext context) {
return Consumer<BottomNavigationTab>(
builder: (BuildContext context, BottomNavigationTab tab, Widget? child) {
if (pendingResume && _isScreenVisible(tab: tab)) {
pendingResume = false;
_onResumeImageStream();
}

return child!;
return Listener<UserPreferences>(
listener: (_, __, UserPreferences prefs) {
_controller?.reloadImageMode();
},
// [_startLiveFeed] is called both with [onResume] and [onPause] to cover
// all entry points
child: LifeCycleManager(
onStart: _startLiveFeed,
onResume: _onResumeImageStream,
onVisible: () => _onResumeImageStream(forceStartPreview: true),
onPause: _onPauseImageStream,
onInvisible: _onPauseImageStream,
child: _buildScannerWidget(),
child: Consumer<BottomNavigationTab>(
builder:
(BuildContext context, BottomNavigationTab tab, Widget? child) {
if (pendingResume && _isScreenVisible(tab: tab)) {
pendingResume = false;
_onResumeImageStream();
}

return child!;
},
// [_startLiveFeed] is called both with [onResume] and [onPause] to cover
// all entry points
child: LifeCycleManager(
onStart: _startLiveFeed,
onResume: _onResumeImageStream,
onVisible: () => _onResumeImageStream(forceStartPreview: true),
onPause: _onPauseImageStream,
onInvisible: _onPauseImageStream,
child: _buildScannerWidget(),
),
),
);
}
Expand Down
10 changes: 5 additions & 5 deletions packages/smooth_app/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ packages:
description:
path: "packages/camera/camera"
ref: smooth_camera
resolved-ref: "40497e4fc74a8512634e04c3c9d6a8c3a50284c0"
resolved-ref: "3c62b56bb6cc0ae1c9594f417ce6305ea45e214f"
url: "https://github.com/g123k/plugins.git"
source: git
version: "0.9.6"
Expand All @@ -148,7 +148,7 @@ packages:
description:
path: "packages/camera/camera_platform_interface"
ref: smooth_camera
resolved-ref: "40497e4fc74a8512634e04c3c9d6a8c3a50284c0"
resolved-ref: "3c62b56bb6cc0ae1c9594f417ce6305ea45e214f"
url: "https://github.com/g123k/plugins.git"
source: git
version: "2.1.6"
Expand All @@ -157,7 +157,7 @@ packages:
description:
path: "packages/camera/camera_web"
ref: smooth_camera
resolved-ref: "40497e4fc74a8512634e04c3c9d6a8c3a50284c0"
resolved-ref: "3c62b56bb6cc0ae1c9594f417ce6305ea45e214f"
url: "https://github.com/g123k/plugins.git"
source: git
version: "0.2.1+6"
Expand Down Expand Up @@ -300,7 +300,7 @@ packages:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.3"
version: "2.2.4"
data_importer:
dependency: "direct main"
description:
Expand Down Expand Up @@ -1366,7 +1366,7 @@ packages:
name: url_launcher_android
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.18"
version: "6.0.19"
url_launcher_ios:
dependency: transitive
description:
Expand Down
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 0 additions & 10 deletions packages/smooth_app/test/plural_translation_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -145,16 +145,6 @@ void main() {
appLocalizations.email_body_account_deletion(crazyString),
contains(crazyString),
);
expect(
appLocalizations.camera_focus_point_algorithm_subtitle(
crazyString, ''),
contains(crazyString),
);
expect(
appLocalizations.camera_focus_point_algorithm_subtitle(
'', crazyString),
contains(crazyString),
);
expect(
appLocalizations.permission_photo_denied_message(crazyString),
contains(crazyString),
Expand Down

0 comments on commit acb5fac

Please sign in to comment.