Skip to content

Commit

Permalink
ML-143 EV100 indication (#148)
Browse files Browse the repository at this point in the history
* added `showEV100` to user preferences

* integrated EV100 setting to UI

* available for pro

* replaced `IAPProducts.isPurchased` with context extension

* fixed `UserPreferencesProvider` tests

* EV100 -> Ev100
  • Loading branch information
vodemn authored Jan 15, 2024
1 parent 73d0c32 commit 8f5893c
Show file tree
Hide file tree
Showing 21 changed files with 141 additions and 24 deletions.
4 changes: 4 additions & 0 deletions lib/data/shared_prefs_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class UserPreferencesService {

static const evSourceTypeKey = "evSourceType";
static const stopTypeKey = "stopType";
static const showEv100Key = "showEv100";
static const cameraEvCalibrationKey = "cameraEvCalibration";
static const lightSensorEvCalibrationKey = "lightSensorEvCalibration";
static const meteringScreenLayoutKey = "meteringScreenLayout";
Expand Down Expand Up @@ -84,6 +85,9 @@ class UserPreferencesService {
StopType get stopType => StopType.values[_sharedPreferences.getInt(stopTypeKey) ?? 2];
set stopType(StopType value) => _sharedPreferences.setInt(stopTypeKey, value.index);

bool get showEv100 => _sharedPreferences.getBool(showEv100Key) ?? false;
set showEv100(bool value) => _sharedPreferences.setBool(showEv100Key, value);

MeteringScreenLayoutConfig get meteringScreenLayout {
final configJson = _sharedPreferences.getString(meteringScreenLayoutKey);
if (configJson != null) {
Expand Down
1 change: 1 addition & 0 deletions lib/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"calibrationMessageCameraOnly": "The accuracy of the readings measured by this application depends entirely on the rear camera of the device. Therefore, consider testing this application and setting up an EV calibration value that will give you the desired measurement results.",
"camera": "Camera",
"lightSensor": "Light sensor",
"showEv100": "Show EV\u2081\u2080\u2080",
"meteringScreenLayout": "Metering screen layout",
"meteringScreenLayoutHint": "Hide elements on the metering screen that you don't need so that they don't waste exposure pairs list space.",
"meteringScreenLayoutHintEquipmentProfiles": "Equipment profile picker",
Expand Down
1 change: 1 addition & 0 deletions lib/l10n/intl_fr.arb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"calibrationMessageCameraOnly": "La précision des lectures mesurées par cette application dépend entièrement de la caméra arrière de l'appareil. Par conséquent, envisagez de tester cette application et de configurer une valeur d'étalonnage EV qui vous donnera les résultats de mesure souhaités.",
"camera": "Caméra",
"lightSensor": "Capteur de lumière",
"showEv100": "Montrer EV\u2081\u2080\u2080",
"meteringScreenLayout": "Disposition de l'écran de mesure",
"meteringScreenLayoutHint": "Masquer les éléments sur l'écran de mesure dont vous n'avez pas besoin pour qu'ils ne gaspillent pas de l'espace dans les paires d'exposition.",
"meteringScreenLayoutHintEquipmentProfiles": "Sélecteur de profil de l'équipement",
Expand Down
1 change: 1 addition & 0 deletions lib/l10n/intl_ru.arb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"calibrationMessageCameraOnly": "Точность измерений данного приложения полностью зависит от точности камеры вашего устройства. Поэтому рекомендуется самостоятельно подобрать калибровочное значение, которое даст желаемый результат измерений.",
"camera": "Камера",
"lightSensor": "Датчик освещённости",
"showEv100": "Показывать EV\u2081\u2080\u2080",
"meteringScreenLayout": "Элементы главного экрана",
"meteringScreenLayoutHint": "Здесь вы можете скрыть некоторые ненужные или неиспользуемые элементы с главного экрана.",
"meteringScreenLayoutHintEquipmentProfiles": "Выбор профиля оборудования",
Expand Down
1 change: 1 addition & 0 deletions lib/l10n/intl_zh.arb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"calibrationMessageCameraOnly": "此应用程序测量读数的准确s性完全取决于设备的后置摄像头。因此,请考虑测试此应用并手动设置 EV 校准,以获得准确的测量结果。",
"camera": "摄像头",
"lightSensor": "光传感器",
"showEv100": "显示 EV\u2081\u2080\u2080",
"meteringScreenLayout": "布局",
"meteringScreenLayoutHint": "隐藏不需要的元素,以免浪费曝光列表空间",
"meteringScreenLayoutHintEquipmentProfiles": "设备配置选择",
Expand Down
5 changes: 3 additions & 2 deletions lib/providers/equipment_profile_provider.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/utils/context_utils.dart';
import 'package:lightmeter/utils/selectable_provider.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
Expand Down Expand Up @@ -52,9 +53,9 @@ class EquipmentProfileProviderState extends State<EquipmentProfileProvider> {
return EquipmentProfiles(
values: [
_defaultProfile,
if (IAPProducts.isPurchased(context, IAPProductType.paidFeatures)) ..._customProfiles,
if (context.isPro) ..._customProfiles,
],
selected: IAPProducts.isPurchased(context, IAPProductType.paidFeatures) ? _selectedProfile : _defaultProfile,
selected: context.isPro ? _selectedProfile : _defaultProfile,
child: widget.child,
);
}
Expand Down
7 changes: 3 additions & 4 deletions lib/providers/films_provider.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/utils/context_utils.dart';
import 'package:lightmeter/utils/selectable_provider.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
Expand Down Expand Up @@ -44,11 +45,9 @@ class FilmsProviderState extends State<FilmsProvider> {
],
filmsInUse: [
const Film.other(),
if (IAPProducts.isPurchased(context, IAPProductType.paidFeatures)) ..._filmsInUse,
if (context.isPro) ..._filmsInUse,
],
selected: IAPProducts.isPurchased(context, IAPProductType.paidFeatures)
? _selected
: const Film.other(),
selected: context.isPro ? _selected : const Film.other(),
child: widget.child,
);
}
Expand Down
18 changes: 18 additions & 0 deletions lib/providers/user_preferences_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class UserPreferencesProvider extends StatefulWidget {
return _inheritFromEnumsModel(context, _Aspect.stopType).stopType;
}

static bool showEv100Of(BuildContext context) {
return _inheritFromEnumsModel(context, _Aspect.showEv100).showEv100;
}

static CameraFeaturesConfig cameraConfigOf(BuildContext context) {
return context.findAncestorWidgetOfExactType<_CameraFeaturesModel>()!.data;
}
Expand Down Expand Up @@ -83,6 +87,7 @@ class UserPreferencesProvider extends StatefulWidget {
class _UserPreferencesProviderState extends State<UserPreferencesProvider> with WidgetsBindingObserver {
late EvSourceType _evSourceType;
late StopType _stopType = widget.userPreferencesService.stopType;
late bool _showEv100 = widget.userPreferencesService.showEv100;
late MeteringScreenLayoutConfig _meteringScreenLayout = widget.userPreferencesService.meteringScreenLayout;
late CameraFeaturesConfig _cameraFeatures = widget.userPreferencesService.cameraFeatures;
late SupportedLocale _locale = widget.userPreferencesService.locale;
Expand Down Expand Up @@ -135,6 +140,7 @@ class _UserPreferencesProviderState extends State<UserPreferencesProvider> with
evSourceType: _evSourceType,
locale: _locale,
primaryColor: dynamicPrimaryColor ?? _primaryColor,
showEv100: _showEv100,
stopType: _stopType,
themeType: _themeType,
child: _MeteringScreenLayoutModel(
Expand Down Expand Up @@ -201,6 +207,13 @@ class _UserPreferencesProviderState extends State<UserPreferencesProvider> with
widget.userPreferencesService.primaryColor = primaryColor;
}

void toggleShowEv100() {
setState(() {
_showEv100 = !_showEv100;
});
widget.userPreferencesService.showEv100 = _showEv100;
}

void setStopType(StopType stopType) {
setState(() {
_stopType = stopType;
Expand Down Expand Up @@ -231,6 +244,7 @@ enum _Aspect {
dynamicColorState,
evSourceType,
locale,
showEv100,
stopType,
theme,
themeType,
Expand All @@ -240,6 +254,7 @@ class _UserPreferencesModel extends InheritedModel<_Aspect> {
final DynamicColorState dynamicColorState;
final EvSourceType evSourceType;
final SupportedLocale locale;
final bool showEv100;
final StopType stopType;
final ThemeType themeType;

Expand All @@ -252,6 +267,7 @@ class _UserPreferencesModel extends InheritedModel<_Aspect> {
required this.evSourceType,
required this.locale,
required Color primaryColor,
required this.showEv100,
required this.stopType,
required this.themeType,
required super.child,
Expand All @@ -267,6 +283,7 @@ class _UserPreferencesModel extends InheritedModel<_Aspect> {
evSourceType != oldWidget.evSourceType ||
locale != oldWidget.locale ||
_primaryColor != oldWidget._primaryColor ||
showEv100 != oldWidget.showEv100 ||
stopType != oldWidget.stopType ||
themeType != oldWidget.themeType;
}
Expand All @@ -279,6 +296,7 @@ class _UserPreferencesModel extends InheritedModel<_Aspect> {
return (dependencies.contains(_Aspect.dynamicColorState) && dynamicColorState != oldWidget.dynamicColorState) ||
(dependencies.contains(_Aspect.evSourceType) && evSourceType != oldWidget.evSourceType) ||
(dependencies.contains(_Aspect.locale) && locale != oldWidget.locale) ||
(dependencies.contains(_Aspect.showEv100) && showEv100 != oldWidget.showEv100) ||
(dependencies.contains(_Aspect.stopType) && stopType != oldWidget.stopType) ||
(dependencies.contains(_Aspect.theme) &&
(_brightness != oldWidget._brightness || _primaryColor != oldWidget._primaryColor)) ||
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/providers/user_preferences_provider.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/screens/shared/filled_circle/widget_circle_filled.dart';
import 'package:lightmeter/utils/context_utils.dart';

const String _subscript100 = '\u2081\u2080\u2080';

class MeteringMeasureButton extends StatefulWidget {
final double? ev;
final double? ev100;
final bool isMetering;
final VoidCallback onTap;

const MeteringMeasureButton({
required this.ev,
required this.ev100,
required this.isMetering,
required this.onTap,
super.key,
Expand Down Expand Up @@ -61,7 +67,7 @@ class _MeteringMeasureButtonState extends State<MeteringMeasureButton> {
color: Theme.of(context).colorScheme.onSurface,
size: Dimens.grid72 - Dimens.grid8,
child: Center(
child: widget.ev != null ? _EvValueText(ev: widget.ev!) : null,
child: widget.ev != null ? _EvValueText(ev: widget.ev!, ev100: widget.ev100!) : null,
),
),
),
Expand All @@ -83,16 +89,32 @@ class _MeteringMeasureButtonState extends State<MeteringMeasureButton> {

class _EvValueText extends StatelessWidget {
final double ev;
final double ev100;

const _EvValueText({required this.ev});
const _EvValueText({
required this.ev,
required this.ev100,
});

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Text(
'${ev.toStringAsFixed(1)}\n${S.of(context).ev}',
_text(context),
style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.surface),
textAlign: TextAlign.center,
);
}

String _text(BuildContext context) {
final bool showEv100 = context.isPro && UserPreferencesProvider.showEv100Of(context);
final StringBuffer buffer = StringBuffer()
..writeAll([
(showEv100 ? ev100 : ev).toStringAsFixed(1),
'\n',
S.of(context).ev,
if (showEv100) _subscript100,
]);
return buffer.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import 'package:lightmeter/screens/metering/components/bottom_controls/widget_bo

class MeteringBottomControlsProvider extends StatelessWidget {
final double? ev;
final double? ev100;
final bool isMetering;
final VoidCallback? onSwitchEvSourceType;
final VoidCallback onMeasure;
final VoidCallback onSettings;

const MeteringBottomControlsProvider({
required this.ev,
required this.ev100,
required this.isMetering,
required this.onSwitchEvSourceType,
required this.onMeasure,
Expand All @@ -35,6 +37,7 @@ class MeteringBottomControlsProvider extends StatelessWidget {
),
child: MeteringBottomControls(
ev: ev,
ev100: ev100,
isMetering: isMetering,
onSwitchEvSourceType: onSwitchEvSourceType,
onMeasure: onMeasure,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import 'package:lightmeter/screens/metering/components/bottom_controls/component

class MeteringBottomControls extends StatelessWidget {
final double? ev;
final double? ev100;
final bool isMetering;
final VoidCallback? onSwitchEvSourceType;
final VoidCallback onMeasure;
final VoidCallback onSettings;

const MeteringBottomControls({
required this.ev,
required this.ev100,
required this.isMetering,
required this.onSwitchEvSourceType,
required this.onMeasure,
Expand Down Expand Up @@ -58,6 +60,7 @@ class MeteringBottomControls extends StatelessWidget {
const Spacer(),
MeteringMeasureButton(
ev: ev,
ev100: ev100,
isMetering: isMetering,
onTap: onMeasure,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'package:lightmeter/screens/metering/components/camera_container/componen
import 'package:lightmeter/screens/metering/components/camera_container/components/camera_preview/components/camera_view_placeholder/widget_placeholder_camera_view.dart';
import 'package:lightmeter/screens/metering/components/camera_container/components/camera_preview/components/histogram/widget_histogram.dart';
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:lightmeter/utils/context_utils.dart';

class CameraPreview extends StatefulWidget {
final CameraController? controller;
Expand Down Expand Up @@ -93,7 +93,7 @@ class _CameraPreviewBuilderState extends State<_CameraPreviewBuilder> {
alignment: Alignment.bottomCenter,
children: [
CameraView(controller: widget.controller),
if (IAPProducts.isPurchased(context, IAPProductType.paidFeatures)) ...[
if (context.isPro) ...[
if (UserPreferencesProvider.cameraFeatureOf(
context,
CameraFeature.histogram,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_lis
import 'package:lightmeter/screens/metering/components/shared/metering_top_bar/widget_top_bar_metering.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/widget_container_readings.dart';
import 'package:lightmeter/utils/context_utils.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';

class CameraContainer extends StatelessWidget {
Expand Down Expand Up @@ -102,9 +101,8 @@ class CameraContainer extends StatelessWidget {
}

double _meteringContainerHeight(BuildContext context) {
final isPro = IAPProducts.isPurchased(context, IAPProductType.paidFeatures);
double enabledFeaturesHeight = 0;
if (!isPro) {
if (!context.isPro) {
enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight;
enabledFeaturesHeight += Dimens.paddingS;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import 'package:lightmeter/screens/metering/components/shared/readings_container
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/lightmeter_pro/widget_lightmeter_pro.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/nd_picker/widget_picker_nd.dart';
import 'package:lightmeter/utils/context_utils.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';

class ReadingsContainer extends StatelessWidget {
Expand All @@ -33,15 +32,14 @@ class ReadingsContainer extends StatelessWidget {

@override
Widget build(BuildContext context) {
final isPro = IAPProducts.isPurchased(context, IAPProductType.paidFeatures);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (!isPro) ...[
if (!context.isPro) ...[
const LightmeterProAnimatedDialog(),
const _InnerPadding(),
],
if (isPro && context.meteringFeature(MeteringScreenLayoutFeature.equipmentProfiles)) ...[
if (context.isPro && context.meteringFeature(MeteringScreenLayoutFeature.equipmentProfiles)) ...[
const EquipmentProfilePicker(),
const _InnerPadding(),
],
Expand All @@ -52,7 +50,7 @@ class ReadingsContainer extends StatelessWidget {
),
const _InnerPadding(),
],
if (isPro && context.meteringFeature(MeteringScreenLayoutFeature.filmPicker)) ...[
if (context.isPro && context.meteringFeature(MeteringScreenLayoutFeature.filmPicker)) ...[
FilmPicker(selectedIso: iso),
const _InnerPadding(),
],
Expand Down
1 change: 1 addition & 0 deletions lib/screens/metering/screen_metering.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class MeteringScreen extends StatelessWidget {
BlocBuilder<MeteringBloc, MeteringState>(
builder: (context, state) => MeteringBottomControlsProvider(
ev: state is MeteringDataState ? state.ev : null,
ev100: state is MeteringDataState ? state.ev100 : null,
isMetering: state.isMetering,
onSwitchEvSourceType: ServicesProvider.of(context).environment.hasLightSensor
? UserPreferencesProvider.of(context).toggleEvSourceType
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/providers/user_preferences_provider.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/screens/settings/components/shared/disable/widget_disable.dart';
import 'package:lightmeter/utils/context_utils.dart';

class ShowEv100ListTile extends StatelessWidget {
const ShowEv100ListTile({super.key});

@override
Widget build(BuildContext context) {
return Disable(
disable: !context.isPro,
child: SwitchListTile(
secondary: const Icon(Icons.adjust),
title: Text(S.of(context).showEv100),
value: context.isPro && UserPreferencesProvider.showEv100Of(context),
onChanged: (_) => UserPreferencesProvider.of(context).toggleShowEv100(),
contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
),
);
}
}
Loading

0 comments on commit 8f5893c

Please sign in to comment.