diff --git a/.github/workflows/pr_check.yml b/.github/workflows/pr_check.yml index 70e6fa4..ac420da 100644 --- a/.github/workflows/pr_check.yml +++ b/.github/workflows/pr_check.yml @@ -15,7 +15,7 @@ jobs: analyze_and_test: name: Analyze & test runs-on: macos-11 - timeout-minutes: 10 + timeout-minutes: 5 steps: - uses: actions/checkout@v3 with: @@ -47,3 +47,10 @@ jobs: - name: Run tests run: flutter test + + - name: Analyze project source with stub + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} + run: | + bash ./.github/scripts/stub_iap.sh + flutter pub get + flutter analyze lib --fatal-infos diff --git a/README.md b/README.md index 08e5780..dae7291 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,7 @@ Without further delay behold my new Lightmeter app inspired by Material You (a.k To build this app you need to install Flutter 3.10.0 stable. [How to install](https://docs.flutter.dev/get-started/install). -### 2. (Optional) Install Firebase - -Out of the box Firebase Crashlytics won't work. If you want to add Crashlytics to your local build please follow [this guide](https://firebase.google.com/docs/flutter/setup). - -### 3. Get packages +### 3. Project setup As part of the app's functionallity is in the private repo, you have to replace these lines in _pubspec.yaml_: @@ -47,24 +43,39 @@ m3_lightmeter_iap: url: "https://github.com/vodemn/m3_lightmeter_iap" ref: main ``` + with these: + ```yaml m3_lightmeter_iap: path: iap ``` -and run `flutter pub get` from the _iap/_ folder. + +You can do it simply by running the script: + +```console +sh .github/scripts/stub_iap.sh +``` + +> If you are using VSCode, you can open the workspace like so: _File -> Open Workspace from File -> m3_lightmeter.code-workspace_. Otherwise you have to run `flutter pub get` command from the iap folder. Then you can fetch all the neccessary dependencies and generate translation files by running the following commands: + ```console flutter pub get flutter pub run intl_utils:generate ``` -### 4. Build +### 4. (Optional) Install Firebase + +Out of the box Firebase Crashlytics won't work. If you want to add Crashlytics to your local build please follow [this guide](https://firebase.google.com/docs/flutter/setup). + +### 5. Build #### Android You can build an apk by running the following command from the root of the repository: + ```console flutter build apk --release --flavor dev --dart-define cameraPreviewAspectRatio=240/320 -t lib/main_dev.dart ``` diff --git a/iap/lib/m3_lightmeter_iap.dart b/iap/lib/m3_lightmeter_iap.dart index 8fe8aa5..171fe47 100644 --- a/iap/lib/m3_lightmeter_iap.dart +++ b/iap/lib/m3_lightmeter_iap.dart @@ -2,11 +2,13 @@ library m3_lightmeter_iap; import 'package:flutter/material.dart'; import 'package:m3_lightmeter_iap/src/providers/equipment_profile_provider.dart'; +import 'package:m3_lightmeter_iap/src/providers/films_provider.dart'; import 'package:m3_lightmeter_iap/src/providers/iap_products_provider.dart'; export 'src/data/models/iap_product.dart'; -export 'src/providers/equipment_profile_provider.dart' hide EquipmentProfilesAspect; +export 'src/providers/equipment_profile_provider.dart'; +export 'src/providers/films_provider.dart'; export 'src/providers/iap_products_provider.dart'; class IAPProviders extends StatelessWidget { @@ -22,8 +24,10 @@ class IAPProviders extends StatelessWidget { @override Widget build(BuildContext context) { return IAPProductsProvider( - child: EquipmentProfileProvider( - child: child, + child: FilmsProvider( + child: EquipmentProfileProvider( + child: child, + ), ), ); } diff --git a/iap/lib/src/providers/equipment_profile_provider.dart b/iap/lib/src/providers/equipment_profile_provider.dart index 4f7aa0f..92ba8a2 100644 --- a/iap/lib/src/providers/equipment_profile_provider.dart +++ b/iap/lib/src/providers/equipment_profile_provider.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:m3_lightmeter_iap/src/providers/selectable_provider.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class EquipmentProfileProvider extends StatefulWidget { @@ -27,7 +28,7 @@ class EquipmentProfileProviderState extends State { @override Widget build(BuildContext context) { return EquipmentProfiles( - profiles: const [_defaultProfile], + values: const [_defaultProfile], selected: _defaultProfile, child: widget.child, ); @@ -42,38 +43,19 @@ class EquipmentProfileProviderState extends State { void deleteProfile(EquipmentProfile data) {} } -enum EquipmentProfilesAspect { list, selected } - -class EquipmentProfiles extends InheritedModel { +class EquipmentProfiles extends SelectableInheritedModel { const EquipmentProfiles({ super.key, - required this.profiles, - required this.selected, + required super.values, + required super.selected, required super.child, }); - final List profiles; - final EquipmentProfile selected; - static List of(BuildContext context) { - return InheritedModel.inheritFrom( - context, - aspect: EquipmentProfilesAspect.list, - )! - .profiles; + return InheritedModel.inheritFrom(context, aspect: SelectableAspect.list)!.values; } static EquipmentProfile selectedOf(BuildContext context) { - return InheritedModel.inheritFrom( - context, - aspect: EquipmentProfilesAspect.selected, - )! - .selected; + return InheritedModel.inheritFrom(context, aspect: SelectableAspect.selected)!.selected; } - - @override - bool updateShouldNotify(EquipmentProfiles oldWidget) => false; - - @override - bool updateShouldNotifyDependent(EquipmentProfiles oldWidget, Set dependencies) => false; } diff --git a/iap/lib/src/providers/films_provider.dart b/iap/lib/src/providers/films_provider.dart new file mode 100644 index 0000000..e75ccd3 --- /dev/null +++ b/iap/lib/src/providers/films_provider.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:m3_lightmeter_iap/src/providers/selectable_provider.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + +class FilmsProvider extends StatefulWidget { + final Widget child; + + const FilmsProvider({ + required this.child, + super.key, + }); + + static FilmsProviderState of(BuildContext context) { + return context.findAncestorStateOfType()!; + } + + @override + State createState() => FilmsProviderState(); +} + +class FilmsProviderState extends State { + @override + Widget build(BuildContext context) { + return Films( + values: const [Film.other()], + filmsInUse: const [Film.other()], + selected: const Film.other(), + child: widget.child, + ); + } + + void setFilm(Film film) {} + + void saveFilms(List films) {} +} + +class Films extends SelectableInheritedModel { + final List filmsInUse; + + const Films({ + super.key, + required super.values, + required this.filmsInUse, + required super.selected, + required super.child, + }); + + /// [Film.other()] + all the custom fields with actual reciprocity formulas + static List of(BuildContext context) { + return InheritedModel.inheritFrom(context)!.values; + } + + /// [Film.other()] + films in use selected by user + static List inUseOf(BuildContext context) { + return InheritedModel.inheritFrom( + context, + aspect: SelectableAspect.list, + )! + .filmsInUse; + } + + static Film selectedOf(BuildContext context) { + return InheritedModel.inheritFrom(context, aspect: SelectableAspect.selected)!.selected; + } +} diff --git a/iap/lib/src/providers/selectable_provider.dart b/iap/lib/src/providers/selectable_provider.dart new file mode 100644 index 0000000..c18f998 --- /dev/null +++ b/iap/lib/src/providers/selectable_provider.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +enum SelectableAspect { list, selected } + +class SelectableInheritedModel extends InheritedModel { + const SelectableInheritedModel({ + super.key, + required this.values, + required this.selected, + required super.child, + }); + + final List values; + final T selected; + + @override + bool updateShouldNotify(SelectableInheritedModel oldWidget) => true; + + @override + bool updateShouldNotifyDependent(SelectableInheritedModel oldWidget, Set dependencies) { + if (dependencies.contains(SelectableAspect.list)) { + return true; + } else if (dependencies.contains(SelectableAspect.selected)) { + return selected != oldWidget.selected; + } else { + return false; + } + } +} diff --git a/lib/data/models/film.dart b/lib/data/models/film.dart deleted file mode 100644 index ab651e8..0000000 --- a/lib/data/models/film.dart +++ /dev/null @@ -1,235 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/foundation.dart'; -import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; - -double log10(double x) => log(x) / log(10); - -double log10polynomian( - double x, - double a, - double b, - double c, -) => - a * pow(log10(x), 2) + b * log10(x) + c; - -typedef ReciprocityFailureBuilder = ShutterSpeedValue Function(ShutterSpeedValue shutterSpeed); - -/// Only Ilford films have reciprocity formulas provided by the manufacturer: -/// https://www.ilfordphoto.com/wp/wp-content/uploads/2017/06/Reciprocity-Failure-Compensation.pdf -/// -/// Reciprocity formulas for Fomapan films and Kodak films are from here: -/// https://www.flickr.com/groups/86738082@N00/discuss/72157626050157470/ -/// -/// Cinema films like Kodak 5222/7222 Double-X and respective CineStill films (cause they are basically a modification of Kodak) -/// do not have any reciprocity failure information, as these films are ment to be used in cinema -/// with appropriate light and pretty short shutter speeds. -/// -/// Because of this: https://github.com/dart-lang/sdk/issues/38934#issuecomment-803938315 -/// `super` calls are ignored in test coverage -class Film { - final String name; - final int iso; - - const Film(this.name, this.iso); - - const Film.other() - : name = '', - iso = 0; - - @override - String toString() => name; - - ShutterSpeedValue reciprocityFailure(ShutterSpeedValue shutterSpeed) { - if (shutterSpeed.isFraction) { - return shutterSpeed; - } else { - return ShutterSpeedValue( - reciprocityFormula(shutterSpeed.rawValue), - shutterSpeed.isFraction, - shutterSpeed.stopType, - ); - } - } - - @protected - double reciprocityFormula(double t) => t; - - static const List values = [ - Film.other(), - FomapanFilm.creative100(), - FomapanFilm.creative200(), - FomapanFilm.action400(), - IlfordFilm.ortho(), - IlfordFilm.delta100(), - IlfordFilm.delta400(), - IlfordFilm.delta3200(), - IlfordFilm.fp4(), - IlfordFilm.hp5(), - IlfordFilm.panf(), - IlfordFilm.sfx200(), - IlfordFilm.xp2super(), - IlfordFilm.pan100(), - IlfordFilm.pan400(), - KodakFilm.tmax100(), - KodakFilm.tmax400(), - KodakFilm.tmax3200(), - KodakFilm.trix320(), - KodakFilm.trix400(), - ]; -} - -/// https://www.tate.org.uk/documents/598/page_6_7_agfa_stocks_0.pdf -/// https://www.filmwasters.com/forum/index.php?topic=5298.0 -// {{1,1.87},{2,3.73},{3,8.06},{4,13.93},{5,21.28},{6,23.00},{7,30.12},{8,38.05},{9,44.75},{10,50.12},{20,117},{30,202},{40,293},{50,413},{60,547},{70,694},{80,853},{90,1022},{100,1202}}; -// class AgfaFilm extends Film { -// final double a; -// final double b; -// final double c; - -// const AgfaFilm.apx100() -// : a = 1, -// b = 5, -// c = 2, -// super('Agfa APX 100', 100); // coverage:ignore-line - -// const AgfaFilm.apx400() -// : a = 1.5, -// b = 4.5, -// c = 3, -// super('Agfa APX 400', 400); // coverage:ignore-line - -// @override -// double reciprocityFormula(double t) => t * log10polynomian(t, a, b, c); -// } - -class FomapanFilm extends Film { - final double a; - final double b; - final double c; - - /// https://www.foma.cz/en/fomapan-100 - const FomapanFilm.creative100() - : a = 1, - b = 5, - c = 2, - super('Fomapan CREATIVE 100', 100); // coverage:ignore-line - - /// https://www.foma.cz/en/fomapan-200 - const FomapanFilm.creative200() - : a = 1.5, - b = 4.5, - c = 3, - super('Fomapan CREATIVE 200', 200); // coverage:ignore-line - - /// https://www.foma.cz/en/fomapan-100 - const FomapanFilm.action400() - : a = -1.25, // coverage:ignore-line - b = 5.75, - c = 1.5, - super('Fomapan ACTION 400', 400); // coverage:ignore-line - - @override - double reciprocityFormula(double t) => t * log10polynomian(t, a, b, c); -} - -class IlfordFilm extends Film { - final double reciprocityPower; - - /// https://www.ilfordphoto.com/amfile/file/download/file/1948/product/1650/ - const IlfordFilm.ortho() - : reciprocityPower = 1.25, - super('Ilford ORTHO+', 80); // coverage:ignore-line - - /// https://www.ilfordphoto.com/amfile/file/download/file/1919/product/686/ - const IlfordFilm.fp4() - : reciprocityPower = 1.26, - super('Ilford FP4+', 125); // coverage:ignore-line - - /// https://www.ilfordphoto.com/amfile/file/download/file/1903/product/691/ - const IlfordFilm.hp5() - : reciprocityPower = 1.31, - super('Ilford HP5+', 400); // coverage:ignore-line - - /// https://www.ilfordphoto.com/amfile/file/download/file/3/product/679/ - const IlfordFilm.delta100() - : reciprocityPower = 1.26, - super('Ilford DELTA 100', 100); // coverage:ignore-line - - /// https://www.ilfordphoto.com/amfile/file/download/file/1915/product/684/ - const IlfordFilm.delta400() - : reciprocityPower = 1.41, - super('Ilford DELTA 400', 400); // coverage:ignore-line - - /// https://www.ilfordphoto.com/amfile/file/download/file/1913/product/682/ - const IlfordFilm.delta3200() - : reciprocityPower = 1.33, - super('Ilford DELTA 3200', 3200); // coverage:ignore-line - - /// https://www.ilfordphoto.com/amfile/file/download/file/1905/product/699/ - const IlfordFilm.panf() - : reciprocityPower = 1.33, - super('Ilford Pan F+', 50); // coverage:ignore-line - - /// https://www.ilfordphoto.com/amfile/file/download/file/1907/product/701/ - const IlfordFilm.sfx200() - : reciprocityPower = 1.31, - super('Ilford SFX 200', 200); // coverage:ignore-line - - /// https://www.ilfordphoto.com/amfile/file/download/file/1909/product/703/ - const IlfordFilm.xp2super() - : reciprocityPower = 1.31, - super('Ilford XP2 SUPER', 400); // coverage:ignore-line - - /// https://www.ilfordphoto.com/amfile/file/download/file/1958/product/696/ - const IlfordFilm.pan100() - : reciprocityPower = 1.26, - super('Kentemere 100', 100); // coverage:ignore-line - - /// https://www.ilfordphoto.com/amfile/file/download/file/1959/product/697/ - const IlfordFilm.pan400() - : reciprocityPower = 1.30, - super('Kentemere 400', 400); // coverage:ignore-line - - @override - double reciprocityFormula(double t) => pow(t, reciprocityPower).toDouble(); -} - -class KodakFilm extends Film { - final double a; - final double b; - final double c; - - const KodakFilm.tmax100() - : a = 1 / 6, // coverage:ignore-line - b = 0, // coverage:ignore-line - c = 4 / 3, // coverage:ignore-line - super('Kodak T-MAX 100', 100); // coverage:ignore-line - - const KodakFilm.tmax400() - : a = 2 / 3, // coverage:ignore-line - b = -1 / 2, // coverage:ignore-line - c = 4 / 3, // coverage:ignore-line - super('Kodak T-MAX 400', 400); // coverage:ignore-line - - const KodakFilm.tmax3200() - : a = 7 / 6, // coverage:ignore-line - b = -1, // coverage:ignore-line - c = 4 / 3, // coverage:ignore-line - super('Kodak T-MAX 3200', 3200); // coverage:ignore-line - - const KodakFilm.trix320() - : a = 2, - b = 1, - c = 2, - super('Kodak TRI-X 320', 320); // coverage:ignore-line - - const KodakFilm.trix400() - : a = 2, - b = 1, - c = 2, - super('Kodak TRI-X 400', 400); // coverage:ignore-line - - @override - double reciprocityFormula(double t) => t * log10polynomian(t, a, b, c); -} diff --git a/lib/data/shared_prefs_service.dart b/lib/data/shared_prefs_service.dart index 812f0e6..96d7d9d 100644 --- a/lib/data/shared_prefs_service.dart +++ b/lib/data/shared_prefs_service.dart @@ -2,7 +2,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:lightmeter/data/models/ev_source_type.dart'; -import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/data/models/metering_screen_layout_config.dart'; import 'package:lightmeter/data/models/supported_locale.dart'; import 'package:lightmeter/data/models/theme_type.dart'; @@ -19,7 +18,6 @@ class UserPreferencesService { static const cameraEvCalibrationKey = "cameraEvCalibration"; static const lightSensorEvCalibrationKey = "lightSensorEvCalibration"; static const meteringScreenLayoutKey = "meteringScreenLayout"; - static const filmKey = "film"; static const caffeineKey = "caffeine"; static const hapticsKey = "haptics"; @@ -142,10 +140,4 @@ class UserPreferencesService { bool get dynamicColor => _sharedPreferences.getBool(dynamicColorKey) ?? false; set dynamicColor(bool value) => _sharedPreferences.setBool(dynamicColorKey, value); - - Film get film => Film.values.firstWhere( - (e) => e.name == _sharedPreferences.getString(filmKey), - orElse: () => Film.values.first, - ); - set film(Film value) => _sharedPreferences.setString(filmKey, value.name); } diff --git a/lib/interactors/metering_interactor.dart b/lib/interactors/metering_interactor.dart index 7ea25ac..b4427de 100644 --- a/lib/interactors/metering_interactor.dart +++ b/lib/interactors/metering_interactor.dart @@ -2,7 +2,6 @@ import 'package:app_settings/app_settings.dart'; import 'package:lightmeter/data/caffeine_service.dart'; import 'package:lightmeter/data/haptics_service.dart'; import 'package:lightmeter/data/light_sensor_service.dart'; -import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/data/models/volume_action.dart'; import 'package:lightmeter/data/permissions_service.dart'; import 'package:lightmeter/data/shared_prefs_service.dart'; @@ -45,9 +44,6 @@ class MeteringInteractor { NdValue get ndFilter => _userPreferencesService.ndFilter; set ndFilter(NdValue value) => _userPreferencesService.ndFilter = value; - Film get film => _userPreferencesService.film; - set film(Film value) => _userPreferencesService.film = value; - VolumeAction get volumeAction => _userPreferencesService.volumeAction; /// Executes vibration if haptics are enabled in settings diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 3d3ec6b..364deec 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -41,6 +41,9 @@ "meteringScreenFeatureFilmPicker": "Film picker", "meteringScreenFeatureHistogram": "Histogram", "film": "Film", + "filmPush": "Film (push)", + "filmPull": "Film (pull)", + "filmReciprocityHint": "Applies correction for shutter speeds grater than 1 second", "equipment": "Equipment", "equipmentProfileName": "Equipment profile name", "equipmentProfileNameHint": "Praktica MTL5B", @@ -56,6 +59,8 @@ "equipmentProfile": "Equipment profile", "equipmentProfiles": "Equipment profiles", "tapToAdd": "Tap to add", + "filmsInUse": "Films in use", + "filmsInUseDescription": "Select films which you use.", "general": "General", "keepScreenOn": "Keep screen on", "haptics": "Haptics", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 4c6f353..22b0356 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -41,9 +41,11 @@ "meteringScreenFeatureFilmPicker": "Sélecteur de film", "meteringScreenFeatureHistogram": "Histogramme", "film": "Pellicule", + "filmPush": "Pellicule (push)", + "filmPull": "Pellicule (pull)", + "filmReciprocityHint": "La correction s'applique aux vitesses d'obturation supérieures à 1 seconde", "equipment": "Équipement", "equipmentProfileName": "Nom du profil de l'équipement", - "tapToAdd": "Appuie pour ajouter", "equipmentProfileNameHint": "Praktica MTL5B", "equipmentProfileAllValues": "Tout", "apertureValues": "Valeurs Aperture", @@ -56,6 +58,9 @@ "isoValuesFilterDescription": "Sélectionnez les valeurs ISO à afficher. Ce sont peut-être vos valeurs les plus couramment utilisées ou celles prises en charge par votre caméra.", "equipmentProfile": "Profil de l'équipement", "equipmentProfiles": "Profils de l'équipement", + "tapToAdd": "Appuie pour ajouter", + "filmsInUse": "Films en usage", + "filmsInUseDescription": "Sélectionnez les films que vous utilisez.", "general": "Général", "keepScreenOn": "Garder l'écran allumé", "haptics": "Haptiques", diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 7a7c772..9ce0d9b 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -41,6 +41,9 @@ "meteringScreenFeatureFilmPicker": "Выбор пленки", "meteringScreenFeatureHistogram": "Гистограмма", "film": "Пленка", + "filmPush": "Пленка (push)", + "filmPull": "Пленка (pull)", + "filmReciprocityHint": "Применяет коррекцию для выдержек длиннее 1 секунды", "equipment": "Оборудование", "equipmentProfileName": "Название профиля", "equipmentProfileNameHint": "Praktica MTL5B", @@ -56,6 +59,8 @@ "equipmentProfile": "Оборудование", "equipmentProfiles": "Профили оборудования", "tapToAdd": "Нажмите, чтобы добавить", + "filmsInUse": "Используемые пленки", + "filmsInUseDescription": "Выберите пленки, которыми вы пользуетесь.", "general": "Общие", "keepScreenOn": "Запрет блокировки", "haptics": "Вибрация", diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index 8c4fb07..357ef95 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -31,7 +31,7 @@ "thirdStops": "1/3", "calibration": "校准", "calibrationMessage": "此应用测量读数的准确性完全取决于设备的硬件。因此,请考虑测试此应用并手动设置 EV 校准,以获得准确的测量结果。", - "calibrationMessageCameraOnly": "此应用程序测量读数的准确性完全取决于设备的后置摄像头。因此,请考虑测试此应用并手动设置 EV 校准,以获得准确的测量结果。", + "calibrationMessageCameraOnly": "此应用程序测量读数的准确s性完全取决于设备的后置摄像头。因此,请考虑测试此应用并手动设置 EV 校准,以获得准确的测量结果。", "camera": "摄像头", "lightSensor": "光传感器", "meteringScreenLayout": "布局", @@ -41,6 +41,9 @@ "meteringScreenFeatureFilmPicker": "胶片选择", "meteringScreenFeatureHistogram": "直方图", "film": "胶片", + "filmPush": "胶片 (push)", + "filmPull": "胶片 (pull)", + "filmReciprocityHint": "Applies correction for shutter speeds grater than 1 second", "equipment": "设备", "equipmentProfileName": "设备配置名称", "equipmentProfileNameHint": "Praktica MTL5B", @@ -56,6 +59,8 @@ "equipmentProfile": "设备配置", "equipmentProfiles": "设备配置", "tapToAdd": "點擊添加", + "filmsInUse": "Films in use", + "filmsInUseDescription": "Select films which you use.", "general": "通用", "keepScreenOn": "保持屏幕常亮", "haptics": "震动", diff --git a/lib/screens/metering/bloc_metering.dart b/lib/screens/metering/bloc_metering.dart index 8e4a2f6..cbb0356 100644 --- a/lib/screens/metering/bloc_metering.dart +++ b/lib/screens/metering/bloc_metering.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:bloc_concurrency/bloc_concurrency.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/data/models/volume_action.dart'; import 'package:lightmeter/interactors/metering_interactor.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; @@ -29,7 +28,6 @@ class MeteringBloc extends Bloc { ) : super( MeteringDataState( ev100: null, - film: _meteringInteractor.film, iso: _meteringInteractor.iso, nd: _meteringInteractor.ndFilter, isMetering: false, @@ -42,7 +40,6 @@ class MeteringBloc extends Bloc { .listen(onCommunicationState); on(_onEquipmentProfileChanged); - on(_onFilmChanged); on(_onIsoChanged); on(_onNdChanged); on(_onMeasure, transformer: droppable()); @@ -92,12 +89,9 @@ class MeteringBloc extends Bloc { /// Update selected ISO value and discard selected film, if selected equipment profile /// doesn't contain currently selected value IsoValue iso = state.iso; - Film film = state.film; if (!event.equipmentProfileData.isoValues.any((v) => state.iso.value == v.value)) { _meteringInteractor.iso = event.equipmentProfileData.isoValues.first; iso = event.equipmentProfileData.isoValues.first; - _meteringInteractor.film = Film.values.first; - film = Film.values.first; willUpdateMeasurements = true; } @@ -113,7 +107,6 @@ class MeteringBloc extends Bloc { emit( MeteringDataState( ev100: state.ev100, - film: film, iso: iso, nd: nd, isMetering: state.isMetering, @@ -122,46 +115,12 @@ class MeteringBloc extends Bloc { } } - void _onFilmChanged(FilmChangedEvent event, Emitter emit) { - if (state.film.name != event.film.name) { - _meteringInteractor.film = event.film; - - /// Find `IsoValue` with matching value - IsoValue iso = state.iso; - if (state.iso.value != event.film.iso && event.film != const Film.other()) { - iso = IsoValue.values.firstWhere( - (e) => e.value == event.film.iso, - orElse: () => state.iso, - ); - _meteringInteractor.iso = iso; - } - - /// If user selects 'Other' film we preserve currently selected ISO - /// and therefore only discard reciprocity formula - emit( - MeteringDataState( - ev100: state.ev100, - film: event.film, - iso: iso, - nd: state.nd, - isMetering: state.isMetering, - ), - ); - } - } - void _onIsoChanged(IsoChangedEvent event, Emitter emit) { - /// Discard currently selected film even if ISO is the same, - /// because, for example, Fomapan 400 and any Ilford 400 - /// have different reciprocity formulas - _meteringInteractor.film = Film.values.first; - if (state.iso != event.isoValue) { _meteringInteractor.iso = event.isoValue; emit( MeteringDataState( ev100: state.ev100, - film: Film.values.first, iso: event.isoValue, nd: state.nd, isMetering: state.isMetering, @@ -176,7 +135,6 @@ class MeteringBloc extends Bloc { emit( MeteringDataState( ev100: state.ev100, - film: state.film, iso: state.iso, nd: event.ndValue, isMetering: state.isMetering, @@ -190,7 +148,6 @@ class MeteringBloc extends Bloc { _communicationBloc.add(const communication_events.MeasureEvent()); emit( LoadingState( - film: state.film, iso: state.iso, nd: state.nd, ), @@ -209,7 +166,6 @@ class MeteringBloc extends Bloc { emit( MeteringDataState( ev100: event.ev100, - film: state.film, iso: state.iso, nd: state.nd, isMetering: event.isMetering, @@ -221,7 +177,6 @@ class MeteringBloc extends Bloc { emit( MeteringDataState( ev100: null, - film: state.film, iso: state.iso, nd: state.nd, isMetering: event.isMetering, diff --git a/lib/screens/metering/components/camera_container/components/camera_preview/widget_camera_preview.dart b/lib/screens/metering/components/camera_container/components/camera_preview/widget_camera_preview.dart index ecfcb42..1ae0ca9 100644 --- a/lib/screens/metering/components/camera_container/components/camera_preview/widget_camera_preview.dart +++ b/lib/screens/metering/components/camera_container/components/camera_preview/widget_camera_preview.dart @@ -31,27 +31,7 @@ class _CameraPreviewState extends State { AnimatedSwitcher( duration: Dimens.switchDuration, child: widget.controller != null - ? ValueListenableBuilder( - valueListenable: widget.controller!, - builder: (_, __, ___) => widget.controller!.value.isInitialized - ? Stack( - alignment: Alignment.bottomCenter, - children: [ - CameraView(controller: widget.controller!), - if (UserPreferencesProvider.meteringScreenFeatureOf( - context, - MeteringScreenLayoutFeature.histogram, - )) - Positioned( - left: Dimens.grid8, - right: Dimens.grid8, - bottom: Dimens.grid16, - child: CameraHistogram(controller: widget.controller!), - ), - ], - ) - : const SizedBox.shrink(), - ) + ? _CameraPreviewBuilder(controller: widget.controller!) : CameraViewPlaceholder(error: widget.error), ), ], @@ -60,3 +40,59 @@ class _CameraPreviewState extends State { ); } } + +class _CameraPreviewBuilder extends StatefulWidget { + final CameraController controller; + + const _CameraPreviewBuilder({required this.controller}); + + @override + State<_CameraPreviewBuilder> createState() => _CameraPreviewBuilderState(); +} + +class _CameraPreviewBuilderState extends State<_CameraPreviewBuilder> { + late final ValueNotifier _initializedNotifier = + ValueNotifier(widget.controller.value.isInitialized); + + @override + void initState() { + super.initState(); + widget.controller.addListener(_update); + } + + @override + void dispose() { + widget.controller.removeListener(_update); + _initializedNotifier.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: _initializedNotifier, + builder: (context, value, child) => value + ? Stack( + alignment: Alignment.bottomCenter, + children: [ + CameraView(controller: widget.controller), + if (UserPreferencesProvider.meteringScreenFeatureOf( + context, + MeteringScreenLayoutFeature.histogram, + )) + Positioned( + left: Dimens.grid8, + right: Dimens.grid8, + bottom: Dimens.grid16, + child: CameraHistogram(controller: widget.controller), + ), + ], + ) + : const SizedBox.shrink(), + ); + } + + void _update() { + _initializedNotifier.value = widget.controller.value.isInitialized; + } +} diff --git a/lib/screens/metering/components/camera_container/provider_container_camera.dart b/lib/screens/metering/components/camera_container/provider_container_camera.dart index 0e27700..1d6d8c0 100644 --- a/lib/screens/metering/components/camera_container/provider_container_camera.dart +++ b/lib/screens/metering/components/camera_container/provider_container_camera.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; -import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/components/camera_container/bloc_container_camera.dart'; import 'package:lightmeter/screens/metering/components/camera_container/event_container_camera.dart'; @@ -12,10 +11,8 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class CameraContainerProvider extends StatelessWidget { final ExposurePair? fastest; final ExposurePair? slowest; - final Film film; final IsoValue iso; final NdValue nd; - final ValueChanged onFilmChanged; final ValueChanged onIsoChanged; final ValueChanged onNdChanged; final List exposurePairs; @@ -23,10 +20,8 @@ class CameraContainerProvider extends StatelessWidget { const CameraContainerProvider({ required this.fastest, required this.slowest, - required this.film, required this.iso, required this.nd, - required this.onFilmChanged, required this.onIsoChanged, required this.onNdChanged, required this.exposurePairs, @@ -44,10 +39,8 @@ class CameraContainerProvider extends StatelessWidget { child: CameraContainer( fastest: fastest, slowest: slowest, - film: film, iso: iso, nd: nd, - onFilmChanged: onFilmChanged, onIsoChanged: onIsoChanged, onNdChanged: onNdChanged, exposurePairs: exposurePairs, diff --git a/lib/screens/metering/components/camera_container/widget_container_camera.dart b/lib/screens/metering/components/camera_container/widget_container_camera.dart index 0514d92..944aa34 100644 --- a/lib/screens/metering/components/camera_container/widget_container_camera.dart +++ b/lib/screens/metering/components/camera_container/widget_container_camera.dart @@ -3,7 +3,6 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; -import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/data/models/metering_screen_layout_config.dart'; import 'package:lightmeter/platform_config.dart'; import 'package:lightmeter/providers/user_preferences_provider.dart'; @@ -23,10 +22,8 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class CameraContainer extends StatelessWidget { final ExposurePair? fastest; final ExposurePair? slowest; - final Film film; final IsoValue iso; final NdValue nd; - final ValueChanged onFilmChanged; final ValueChanged onIsoChanged; final ValueChanged onNdChanged; final List exposurePairs; @@ -34,10 +31,8 @@ class CameraContainer extends StatelessWidget { const CameraContainer({ required this.fastest, required this.slowest, - required this.film, required this.iso, required this.nd, - required this.onFilmChanged, required this.onIsoChanged, required this.onNdChanged, required this.exposurePairs, @@ -60,10 +55,8 @@ class CameraContainer extends StatelessWidget { readingsContainer: ReadingsContainer( fastest: fastest, slowest: slowest, - film: film, iso: iso, nd: nd, - onFilmChanged: onFilmChanged, onIsoChanged: onIsoChanged, onNdChanged: onNdChanged, ), diff --git a/lib/screens/metering/components/light_sensor_container/provider_container_light_sensor.dart b/lib/screens/metering/components/light_sensor_container/provider_container_light_sensor.dart index aa27504..1d7f822 100644 --- a/lib/screens/metering/components/light_sensor_container/provider_container_light_sensor.dart +++ b/lib/screens/metering/components/light_sensor_container/provider_container_light_sensor.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; -import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/components/light_sensor_container/bloc_container_light_sensor.dart'; import 'package:lightmeter/screens/metering/components/light_sensor_container/widget_container_light_sensor.dart'; @@ -11,10 +10,8 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class LightSensorContainerProvider extends StatelessWidget { final ExposurePair? fastest; final ExposurePair? slowest; - final Film film; final IsoValue iso; final NdValue nd; - final ValueChanged onFilmChanged; final ValueChanged onIsoChanged; final ValueChanged onNdChanged; final List exposurePairs; @@ -22,10 +19,8 @@ class LightSensorContainerProvider extends StatelessWidget { const LightSensorContainerProvider({ required this.fastest, required this.slowest, - required this.film, required this.iso, required this.nd, - required this.onFilmChanged, required this.onIsoChanged, required this.onNdChanged, required this.exposurePairs, @@ -43,10 +38,8 @@ class LightSensorContainerProvider extends StatelessWidget { child: LightSensorContainer( fastest: fastest, slowest: slowest, - film: film, iso: iso, nd: nd, - onFilmChanged: onFilmChanged, onIsoChanged: onIsoChanged, onNdChanged: onNdChanged, exposurePairs: exposurePairs, diff --git a/lib/screens/metering/components/light_sensor_container/widget_container_light_sensor.dart b/lib/screens/metering/components/light_sensor_container/widget_container_light_sensor.dart index 4380f02..2151ee0 100644 --- a/lib/screens/metering/components/light_sensor_container/widget_container_light_sensor.dart +++ b/lib/screens/metering/components/light_sensor_container/widget_container_light_sensor.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; -import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart'; import 'package:lightmeter/screens/metering/components/shared/metering_top_bar/widget_top_bar_metering.dart'; @@ -10,10 +9,8 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class LightSensorContainer extends StatelessWidget { final ExposurePair? fastest; final ExposurePair? slowest; - final Film film; final IsoValue iso; final NdValue nd; - final ValueChanged onFilmChanged; final ValueChanged onIsoChanged; final ValueChanged onNdChanged; final List exposurePairs; @@ -21,10 +18,8 @@ class LightSensorContainer extends StatelessWidget { const LightSensorContainer({ required this.fastest, required this.slowest, - required this.film, required this.iso, required this.nd, - required this.onFilmChanged, required this.onIsoChanged, required this.onNdChanged, required this.exposurePairs, @@ -39,10 +34,8 @@ class LightSensorContainer extends StatelessWidget { readingsContainer: ReadingsContainer( fastest: fastest, slowest: slowest, - film: film, iso: iso, nd: nd, - onFilmChanged: onFilmChanged, onIsoChanged: onIsoChanged, onNdChanged: onNdChanged, ), diff --git a/lib/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart b/lib/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart index 3f33d03..4051c5e 100644 --- a/lib/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart +++ b/lib/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart @@ -5,6 +5,7 @@ import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/components/exposure_pairs_list_item/widget_item_list_exposure_pairs.dart'; import 'package:lightmeter/screens/shared/icon_placeholder/widget_icon_placeholder.dart'; +import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; class ExposurePairsList extends StatelessWidget { final List exposurePairs; @@ -47,7 +48,8 @@ class ExposurePairsList extends StatelessWidget { child: Align( alignment: Alignment.centerLeft, child: ExposurePairsListItem( - exposurePairs[index].shutterSpeed, + Films.selectedOf(context) + .reciprocityFailure(exposurePairs[index].shutterSpeed), tickOnTheLeft: true, ), ), diff --git a/lib/screens/metering/components/shared/readings_container/components/equipment_profile_picker/widget_picker_equipment_profiles.dart b/lib/screens/metering/components/shared/readings_container/components/equipment_profile_picker/widget_picker_equipment_profiles.dart new file mode 100644 index 0000000..f47cd53 --- /dev/null +++ b/lib/screens/metering/components/shared/readings_container/components/equipment_profile_picker/widget_picker_equipment_profiles.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart'; +import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + +class EquipmentProfilePicker extends StatelessWidget { + const EquipmentProfilePicker(); + + @override + Widget build(BuildContext context) { + return AnimatedDialogPicker( + icon: Icons.camera, + title: S.of(context).equipmentProfile, + selectedValue: EquipmentProfiles.selectedOf(context), + values: EquipmentProfiles.of(context), + itemTitleBuilder: (_, value) => Text(value.id.isEmpty ? S.of(context).none : value.name), + onChanged: EquipmentProfileProvider.of(context).setProfile, + closedChild: ReadingValueContainer.singleValue( + value: ReadingValue( + label: S.of(context).equipmentProfile, + value: EquipmentProfiles.selectedOf(context).id.isEmpty + ? S.of(context).none + : EquipmentProfiles.selectedOf(context).name, + ), + ), + ); + } +} diff --git a/lib/screens/metering/components/shared/readings_container/components/extreme_exposure_pairs_container/widget_container_extreme_exposure_pairs.dart b/lib/screens/metering/components/shared/readings_container/components/extreme_exposure_pairs_container/widget_container_extreme_exposure_pairs.dart new file mode 100644 index 0000000..54c786c --- /dev/null +++ b/lib/screens/metering/components/shared/readings_container/components/extreme_exposure_pairs_container/widget_container_extreme_exposure_pairs.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/data/models/exposure_pair.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart'; +import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; + +class ExtremeExposurePairsContainer extends StatelessWidget { + final ExposurePair? fastest; + final ExposurePair? slowest; + + const ExtremeExposurePairsContainer({ + required this.fastest, + required this.slowest, + super.key, + }); + + @override + Widget build(BuildContext context) { + return ReadingValueContainer( + values: [ + ReadingValue( + label: S.of(context).fastestExposurePair, + value: _exposurePairToString(context, fastest), + ), + ReadingValue( + label: S.of(context).slowestExposurePair, + value: _exposurePairToString(context, slowest), + ), + ], + ); + } + + String _exposurePairToString(BuildContext context, ExposurePair? pair) { + if (pair == null) { + return '-'; + } + + return '${pair.aperture} - ${Films.selectedOf(context).reciprocityFailure(pair.shutterSpeed)}'; + } +} diff --git a/lib/screens/metering/components/shared/readings_container/components/film_picker/widget_picker_film.dart b/lib/screens/metering/components/shared/readings_container/components/film_picker/widget_picker_film.dart new file mode 100644 index 0000000..ae1e6fe --- /dev/null +++ b/lib/screens/metering/components/shared/readings_container/components/film_picker/widget_picker_film.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart'; +import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + +class FilmPicker extends StatelessWidget { + final IsoValue selectedIso; + + const FilmPicker({required this.selectedIso}); + + @override + Widget build(BuildContext context) { + return AnimatedDialogPicker( + icon: Icons.camera_roll, + title: S.of(context).film, + subtitle: S.of(context).filmReciprocityHint, + selectedValue: Films.selectedOf(context), + values: Films.inUseOf(context), + itemTitleBuilder: (_, value) => Text(value.name.isEmpty ? S.of(context).none : value.name), + onChanged: FilmsProvider.of(context).setFilm, + closedChild: ReadingValueContainer.singleValue( + value: ReadingValue( + label: _label(context), + value: Films.selectedOf(context).name.isEmpty + ? S.of(context).none + : Films.selectedOf(context).name, + ), + ), + ); + } + + String _label(BuildContext context) { + if (Films.selectedOf(context) == const Film.other() || + Films.selectedOf(context).iso == selectedIso.value) { + return S.of(context).film; + } + + final evDiff = IsoValue( + Films.selectedOf(context).iso, + StopType.full, + ).difference(selectedIso); + + if (evDiff > 0) { + return S.of(context).filmPush; + } else if (evDiff < 0) { + return S.of(context).filmPull; + } else { + return S.of(context).film; + } + } +} diff --git a/lib/screens/metering/components/shared/readings_container/components/iso_picker/widget_picker_iso.dart b/lib/screens/metering/components/shared/readings_container/components/iso_picker/widget_picker_iso.dart new file mode 100644 index 0000000..a85b30c --- /dev/null +++ b/lib/screens/metering/components/shared/readings_container/components/iso_picker/widget_picker_iso.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + +class IsoValuePicker extends StatelessWidget { + final List values; + final IsoValue selectedValue; + final ValueChanged onChanged; + + const IsoValuePicker({ + required this.selectedValue, + required this.values, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return AnimatedDialogPicker( + icon: Icons.iso, + title: S.of(context).iso, + subtitle: S.of(context).filmSpeed, + selectedValue: selectedValue, + values: values, + itemTitleBuilder: (_, value) => Text(value.value.toString()), + // using ascending order, because increase in film speed rises EV + itemTrailingBuilder: (selected, value) => value.value != selected.value + ? Text(S.of(context).evValue(selected.toStringDifference(value))) + : null, + onChanged: onChanged, + closedChild: ReadingValueContainer.singleValue( + value: ReadingValue( + label: S.of(context).iso, + value: selectedValue.value.toString(), + ), + ), + ); + } +} diff --git a/lib/screens/metering/components/shared/readings_container/components/nd_picker/widget_picker_nd.dart b/lib/screens/metering/components/shared/readings_container/components/nd_picker/widget_picker_nd.dart new file mode 100644 index 0000000..eda016c --- /dev/null +++ b/lib/screens/metering/components/shared/readings_container/components/nd_picker/widget_picker_nd.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + +class NdValuePicker extends StatelessWidget { + final List values; + final NdValue selectedValue; + final ValueChanged onChanged; + + const NdValuePicker({ + required this.selectedValue, + required this.values, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return AnimatedDialogPicker( + icon: Icons.filter_b_and_w, + title: S.of(context).nd, + subtitle: S.of(context).ndFilterFactor, + selectedValue: selectedValue, + values: values, + itemTitleBuilder: (_, value) => Text( + value.value == 0 ? S.of(context).none : value.value.toString(), + ), + // using descending order, because ND filter darkens image & lowers EV + itemTrailingBuilder: (selected, value) => value.value != selected.value + ? Text(S.of(context).evValue(value.toStringDifference(selected))) + : null, + onChanged: onChanged, + closedChild: ReadingValueContainer.singleValue( + value: ReadingValue( + label: S.of(context).nd, + value: selectedValue.value.toString(), + ), + ), + ); + } +} diff --git a/lib/screens/metering/components/shared/readings_container/components/animated_dialog_picker/components/animated_dialog/widget_dialog_animated.dart b/lib/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/animated_dialog/widget_dialog_animated.dart similarity index 100% rename from lib/screens/metering/components/shared/readings_container/components/animated_dialog_picker/components/animated_dialog/widget_dialog_animated.dart rename to lib/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/animated_dialog/widget_dialog_animated.dart diff --git a/lib/screens/metering/components/shared/readings_container/components/animated_dialog_picker/components/dialog_picker/widget_picker_dialog.dart b/lib/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/dialog_picker/widget_picker_dialog.dart similarity index 100% rename from lib/screens/metering/components/shared/readings_container/components/animated_dialog_picker/components/dialog_picker/widget_picker_dialog.dart rename to lib/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/dialog_picker/widget_picker_dialog.dart diff --git a/lib/screens/metering/components/shared/readings_container/components/animated_dialog_picker/widget_picker_dialog_animated.dart b/lib/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart similarity index 89% rename from lib/screens/metering/components/shared/readings_container/components/animated_dialog_picker/widget_picker_dialog_animated.dart rename to lib/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart index d123d99..eeeec52 100644 --- a/lib/screens/metering/components/shared/readings_container/components/animated_dialog_picker/widget_picker_dialog_animated.dart +++ b/lib/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; - -import 'package:lightmeter/screens/metering/components/shared/readings_container/components/animated_dialog_picker/components/animated_dialog/widget_dialog_animated.dart'; -import 'package:lightmeter/screens/metering/components/shared/readings_container/components/animated_dialog_picker/components/dialog_picker/widget_picker_dialog.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/animated_dialog/widget_dialog_animated.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/dialog_picker/widget_picker_dialog.dart'; // Has to be stateful, so that [GlobalKey] is not recreated. // Otherwise use will no be able to close the dialog after EV value has changed. diff --git a/lib/screens/metering/components/shared/readings_container/components/reading_value_container/widget_container_reading_value.dart b/lib/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart similarity index 100% rename from lib/screens/metering/components/shared/readings_container/components/reading_value_container/widget_container_reading_value.dart rename to lib/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart diff --git a/lib/screens/metering/components/shared/readings_container/widget_container_readings.dart b/lib/screens/metering/components/shared/readings_container/widget_container_readings.dart index c9f6f54..f10546d 100644 --- a/lib/screens/metering/components/shared/readings_container/widget_container_readings.dart +++ b/lib/screens/metering/components/shared/readings_container/widget_container_readings.dart @@ -1,32 +1,29 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; -import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/data/models/metering_screen_layout_config.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/metering/components/shared/readings_container/components/animated_dialog_picker/widget_picker_dialog_animated.dart'; -import 'package:lightmeter/screens/metering/components/shared/readings_container/components/reading_value_container/widget_container_reading_value.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/equipment_profile_picker/widget_picker_equipment_profiles.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/extreme_exposure_pairs_container/widget_container_extreme_exposure_pairs.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/film_picker/widget_picker_film.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/iso_picker/widget_picker_iso.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/nd_picker/widget_picker_nd.dart'; import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class ReadingsContainer extends StatelessWidget { final ExposurePair? fastest; final ExposurePair? slowest; - final Film film; final IsoValue iso; final NdValue nd; - final ValueChanged onFilmChanged; final ValueChanged onIsoChanged; final ValueChanged onNdChanged; const ReadingsContainer({ required this.fastest, required this.slowest, - required this.film, required this.iso, required this.nd, - required this.onFilmChanged, required this.onIsoChanged, required this.onNdChanged, super.key, @@ -41,24 +38,16 @@ class ReadingsContainer extends StatelessWidget { context, MeteringScreenLayoutFeature.equipmentProfiles, )) ...[ - const _EquipmentProfilePicker(), + const EquipmentProfilePicker(), const _InnerPadding(), ], if (UserPreferencesProvider.meteringScreenFeatureOf( context, MeteringScreenLayoutFeature.extremeExposurePairs, )) ...[ - ReadingValueContainer( - values: [ - ReadingValue( - label: S.of(context).fastestExposurePair, - value: fastest != null ? fastest!.toString() : '-', - ), - ReadingValue( - label: S.of(context).slowestExposurePair, - value: fastest != null ? slowest!.toString() : '-', - ), - ], + ExtremeExposurePairsContainer( + fastest: fastest, + slowest: slowest, ), const _InnerPadding(), ], @@ -66,17 +55,13 @@ class ReadingsContainer extends StatelessWidget { context, MeteringScreenLayoutFeature.filmPicker, )) ...[ - _FilmPicker( - values: Film.values, - selectedValue: film, - onChanged: onFilmChanged, - ), + FilmPicker(selectedIso: iso), const _InnerPadding(), ], Row( children: [ Expanded( - child: _IsoValuePicker( + child: IsoValuePicker( selectedValue: iso, values: EquipmentProfiles.selectedOf(context).isoValues, onChanged: onIsoChanged, @@ -84,7 +69,7 @@ class ReadingsContainer extends StatelessWidget { ), const _InnerPadding(), Expanded( - child: _NdValuePicker( + child: NdValuePicker( selectedValue: nd, values: EquipmentProfiles.selectedOf(context).ndValues, onChanged: onNdChanged, @@ -100,129 +85,3 @@ class ReadingsContainer extends StatelessWidget { class _InnerPadding extends SizedBox { const _InnerPadding() : super(height: Dimens.grid8, width: Dimens.grid8); } - -class _EquipmentProfilePicker extends StatelessWidget { - const _EquipmentProfilePicker(); - - @override - Widget build(BuildContext context) { - return AnimatedDialogPicker( - icon: Icons.camera, - title: S.of(context).equipmentProfile, - selectedValue: EquipmentProfiles.selectedOf(context), - values: EquipmentProfiles.of(context), - itemTitleBuilder: (_, value) => Text(value.id.isEmpty ? S.of(context).none : value.name), - onChanged: EquipmentProfileProvider.of(context).setProfile, - closedChild: ReadingValueContainer.singleValue( - value: ReadingValue( - label: S.of(context).equipmentProfile, - value: EquipmentProfiles.selectedOf(context).id.isEmpty - ? S.of(context).none - : EquipmentProfiles.selectedOf(context).name, - ), - ), - ); - } -} - -class _FilmPicker extends StatelessWidget { - final List values; - final Film selectedValue; - final ValueChanged onChanged; - - const _FilmPicker({ - required this.values, - required this.selectedValue, - required this.onChanged, - }); - - @override - Widget build(BuildContext context) { - return AnimatedDialogPicker( - icon: Icons.camera_roll, - title: S.of(context).film, - selectedValue: selectedValue, - values: values, - itemTitleBuilder: (_, value) => Text(value.name.isEmpty ? S.of(context).none : value.name), - onChanged: onChanged, - closedChild: ReadingValueContainer.singleValue( - value: ReadingValue( - label: S.of(context).film, - value: selectedValue.name.isEmpty ? S.of(context).none : selectedValue.name, - ), - ), - ); - } -} - -class _IsoValuePicker extends StatelessWidget { - final List values; - final IsoValue selectedValue; - final ValueChanged onChanged; - - const _IsoValuePicker({ - required this.selectedValue, - required this.values, - required this.onChanged, - }); - - @override - Widget build(BuildContext context) { - return AnimatedDialogPicker( - icon: Icons.iso, - title: S.of(context).iso, - subtitle: S.of(context).filmSpeed, - selectedValue: selectedValue, - values: values, - itemTitleBuilder: (_, value) => Text(value.value.toString()), - // using ascending order, because increase in film speed rises EV - itemTrailingBuilder: (selected, value) => value.value != selected.value - ? Text(S.of(context).evValue(selected.toStringDifference(value))) - : null, - onChanged: onChanged, - closedChild: ReadingValueContainer.singleValue( - value: ReadingValue( - label: S.of(context).iso, - value: selectedValue.value.toString(), - ), - ), - ); - } -} - -class _NdValuePicker extends StatelessWidget { - final List values; - final NdValue selectedValue; - final ValueChanged onChanged; - - const _NdValuePicker({ - required this.selectedValue, - required this.values, - required this.onChanged, - }); - - @override - Widget build(BuildContext context) { - return AnimatedDialogPicker( - icon: Icons.filter_b_and_w, - title: S.of(context).nd, - subtitle: S.of(context).ndFilterFactor, - selectedValue: selectedValue, - values: values, - itemTitleBuilder: (_, value) => Text( - value.value == 0 ? S.of(context).none : value.value.toString(), - ), - // using descending order, because ND filter darkens image & lowers EV - itemTrailingBuilder: (selected, value) => value.value != selected.value - ? Text(S.of(context).evValue(value.toStringDifference(selected))) - : null, - onChanged: onChanged, - closedChild: ReadingValueContainer.singleValue( - value: ReadingValue( - label: S.of(context).nd, - value: selectedValue.value.toString(), - ), - ), - ); - } -} diff --git a/lib/screens/metering/event_metering.dart b/lib/screens/metering/event_metering.dart index bf3a22d..0a39844 100644 --- a/lib/screens/metering/event_metering.dart +++ b/lib/screens/metering/event_metering.dart @@ -1,4 +1,3 @@ -import 'package:lightmeter/data/models/film.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; sealed class MeteringEvent { @@ -11,12 +10,6 @@ class EquipmentProfileChangedEvent extends MeteringEvent { const EquipmentProfileChangedEvent(this.equipmentProfileData); } -class FilmChangedEvent extends MeteringEvent { - final Film film; - - const FilmChangedEvent(this.film); -} - class IsoChangedEvent extends MeteringEvent { final IsoValue isoValue; diff --git a/lib/screens/metering/screen_metering.dart b/lib/screens/metering/screen_metering.dart index 93d515e..f1d11fb 100644 --- a/lib/screens/metering/screen_metering.dart +++ b/lib/screens/metering/screen_metering.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/ev_source_type.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; -import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/data/models/metering_screen_layout_config.dart'; import 'package:lightmeter/providers/services_provider.dart'; import 'package:lightmeter/providers/user_preferences_provider.dart'; @@ -33,11 +32,8 @@ class MeteringScreen extends StatelessWidget { child: BlocBuilder( builder: (_, state) => MeteringContainerBuidler( ev: state is MeteringDataState ? state.ev : null, - film: state.film, iso: state.iso, nd: state.nd, - onFilmChanged: (value) => - context.read().add(FilmChangedEvent(value)), onIsoChanged: (value) => context.read().add(IsoChangedEvent(value)), onNdChanged: (value) => context.read().add(NdChangedEvent(value)), ), @@ -81,7 +77,7 @@ class _InheritedListeners extends StatelessWidget { feature: MeteringScreenLayoutFeature.filmPicker, onDidChangeDependencies: (value) { if (!value) { - context.read().add(const FilmChangedEvent(Film.other())); + FilmsProvider.of(context).setFilm(const Film.other()); } }, child: child, @@ -92,19 +88,15 @@ class _InheritedListeners extends StatelessWidget { class MeteringContainerBuidler extends StatelessWidget { final double? ev; - final Film film; final IsoValue iso; final NdValue nd; - final ValueChanged onFilmChanged; final ValueChanged onIsoChanged; final ValueChanged onNdChanged; const MeteringContainerBuidler({ required this.ev, - required this.film, required this.iso, required this.nd, - required this.onFilmChanged, required this.onIsoChanged, required this.onNdChanged, }); @@ -116,7 +108,6 @@ class MeteringContainerBuidler extends StatelessWidget { ev!, UserPreferencesProvider.stopTypeOf(context), EquipmentProfiles.selectedOf(context), - film, ) : []; final fastest = exposurePairs.isNotEmpty ? exposurePairs.first : null; @@ -126,10 +117,8 @@ class MeteringContainerBuidler extends StatelessWidget { ? CameraContainerProvider( fastest: fastest, slowest: slowest, - film: film, iso: iso, nd: nd, - onFilmChanged: onFilmChanged, onIsoChanged: onIsoChanged, onNdChanged: onNdChanged, exposurePairs: exposurePairs, @@ -137,10 +126,8 @@ class MeteringContainerBuidler extends StatelessWidget { : LightSensorContainerProvider( fastest: fastest, slowest: slowest, - film: film, iso: iso, nd: nd, - onFilmChanged: onFilmChanged, onIsoChanged: onIsoChanged, onNdChanged: onNdChanged, exposurePairs: exposurePairs, @@ -152,7 +139,6 @@ class MeteringContainerBuidler extends StatelessWidget { double ev, StopType stopType, EquipmentProfile equipmentProfile, - Film film, ) { if (ev.isNaN || ev.isInfinite) { return List.empty(); @@ -195,7 +181,7 @@ class MeteringContainerBuidler extends StatelessWidget { itemsCount, (index) => ExposurePair( apertureValues[index + apertureOffset], - film.reciprocityFailure(shutterSpeedValues[index + shutterSpeedOffset]), + shutterSpeedValues[index + shutterSpeedOffset], ), growable: false, ); diff --git a/lib/screens/metering/state_metering.dart b/lib/screens/metering/state_metering.dart index 909895d..74dc792 100644 --- a/lib/screens/metering/state_metering.dart +++ b/lib/screens/metering/state_metering.dart @@ -1,18 +1,15 @@ import 'package:flutter/material.dart'; -import 'package:lightmeter/data/models/film.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; @immutable abstract class MeteringState { final double? ev100; - final Film film; final IsoValue iso; final NdValue nd; final bool isMetering; const MeteringState({ this.ev100, - required this.film, required this.iso, required this.nd, required this.isMetering, @@ -21,7 +18,6 @@ abstract class MeteringState { class LoadingState extends MeteringState { const LoadingState({ - required super.film, required super.iso, required super.nd, }) : super(isMetering: true); @@ -30,7 +26,6 @@ class LoadingState extends MeteringState { class MeteringDataState extends MeteringState { const MeteringDataState({ required super.ev100, - required super.film, required super.iso, required super.nd, required super.isMetering, diff --git a/lib/screens/metering/utils/equipment_profile_listener.dart b/lib/screens/metering/utils/equipment_profile_listener.dart deleted file mode 100644 index 68d03dc..0000000 --- a/lib/screens/metering/utils/equipment_profile_listener.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; -import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; - -class EquipmentProfileListener extends StatefulWidget { - final ValueChanged onDidChangeDependencies; - final Widget child; - - const EquipmentProfileListener({ - required this.onDidChangeDependencies, - required this.child, - super.key, - }); - - @override - State createState() => _EquipmentProfileListenerState(); -} - -class _EquipmentProfileListenerState extends State { - @override - void didChangeDependencies() { - super.didChangeDependencies(); - widget.onDidChangeDependencies(EquipmentProfiles.selectedOf(context)); - } - - @override - Widget build(BuildContext context) { - return widget.child; - } -} diff --git a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/widget_list_tiles_equipments.dart b/lib/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/widget_list_tiles_equipments.dart similarity index 90% rename from lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/widget_list_tiles_equipments.dart rename to lib/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/widget_list_tiles_equipments.dart index 695e4e3..482c370 100644 --- a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/widget_list_tiles_equipments.dart +++ b/lib/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/widget_list_tiles_equipments.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/generated/l10n.dart'; -import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/components/dialog_filter/widget_dialog_filter.dart'; -import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/components/dialog_range_picker/widget_dialog_picker_range.dart'; +import 'package:lightmeter/screens/settings/components/shared/dialog_filter/widget_dialog_filter.dart'; +import 'package:lightmeter/screens/settings/components/shared/dialog_range_picker/widget_dialog_picker_range.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class EquipmentListTiles extends StatelessWidget { diff --git a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart b/lib/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart similarity index 94% rename from lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart rename to lib/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart index c11cd2c..244ddce 100644 --- a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart +++ b/lib/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart @@ -3,8 +3,8 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:lightmeter/res/dimens.dart'; -import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/widget_list_tiles_equipments.dart'; -import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_name_dialog/widget_dialog_equipment_profile_name.dart'; +import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/widget_list_tiles_equipments.dart'; +import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_name_dialog/widget_dialog_equipment_profile_name.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class EquipmentProfileContainer extends StatefulWidget { diff --git a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_name_dialog/widget_dialog_equipment_profile_name.dart b/lib/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_name_dialog/widget_dialog_equipment_profile_name.dart similarity index 100% rename from lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_name_dialog/widget_dialog_equipment_profile_name.dart rename to lib/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_name_dialog/widget_dialog_equipment_profile_name.dart diff --git a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart b/lib/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart similarity index 92% rename from lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart rename to lib/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart index 5307145..a5b0eca 100644 --- a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart +++ b/lib/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/res/dimens.dart'; -import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart'; -import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_name_dialog/widget_dialog_equipment_profile_name.dart'; +import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart'; +import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_name_dialog/widget_dialog_equipment_profile_name.dart'; import 'package:lightmeter/screens/shared/icon_placeholder/widget_icon_placeholder.dart'; import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart'; import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; diff --git a/lib/screens/settings/components/equipment/components/equipment_profiles/widget_list_tile_equipment_profiles.dart b/lib/screens/settings/components/equipment/components/equipment_profiles/widget_list_tile_equipment_profiles.dart new file mode 100644 index 0000000..0cb5631 --- /dev/null +++ b/lib/screens/settings/components/equipment/components/equipment_profiles/widget_list_tile_equipment_profiles.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart'; +import 'package:lightmeter/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + +class EquipmentProfilesListTile extends StatelessWidget { + const EquipmentProfilesListTile({super.key}); + + @override + Widget build(BuildContext context) { + return IAPListTile( + leading: const Icon(Icons.camera), + title: Text(S.of(context).equipmentProfiles), + onTap: () { + Navigator.of(context).push( + MaterialPageRoute(builder: (_) => const EquipmentProfilesScreen()), + ); + }, + ); + } +} diff --git a/lib/screens/settings/components/equipment/components/films/widget_list_tile_films.dart b/lib/screens/settings/components/equipment/components/films/widget_list_tile_films.dart new file mode 100644 index 0000000..c343e2b --- /dev/null +++ b/lib/screens/settings/components/equipment/components/films/widget_list_tile_films.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/settings/components/shared/dialog_filter/widget_dialog_filter.dart'; +import 'package:lightmeter/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart'; +import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + +class FilmsListTile extends StatelessWidget { + const FilmsListTile({super.key}); + + @override + Widget build(BuildContext context) { + return IAPListTile( + leading: const Icon(Icons.camera_roll), + title: Text(S.of(context).filmsInUse), + onTap: () { + showDialog>( + context: context, + builder: (_) => DialogFilter( + icon: const Icon(Icons.camera_roll), + title: S.of(context).filmsInUse, + description: S.of(context).filmsInUseDescription, + values: Films.of(context).sublist(1), + selectedValues: Films.inUseOf(context), + titleAdapter: (_, value) => value.name, + ), + ).then((values) { + if (values != null) { + FilmsProvider.of(context).saveFilms(values); + } + }); + }, + ); + } +} diff --git a/lib/screens/settings/components/equipment/widget_settings_section_equipment.dart b/lib/screens/settings/components/equipment/widget_settings_section_equipment.dart new file mode 100644 index 0000000..ba3b975 --- /dev/null +++ b/lib/screens/settings/components/equipment/widget_settings_section_equipment.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/widget_list_tile_equipment_profiles.dart'; +import 'package:lightmeter/screens/settings/components/equipment/components/films/widget_list_tile_films.dart'; +import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart'; + +class EquipmentSettingsSection extends StatelessWidget { + const EquipmentSettingsSection({super.key}); + + @override + Widget build(BuildContext context) { + return SettingsSection( + title: S.of(context).equipment, + children: const [ + EquipmentProfilesListTile(), + FilmsListTile(), + ], + ); + } +} diff --git a/lib/screens/settings/components/general/components/language/widget_list_tile_language.dart b/lib/screens/settings/components/general/components/language/widget_list_tile_language.dart index 663c634..eeaaf4c 100644 --- a/lib/screens/settings/components/general/components/language/widget_list_tile_language.dart +++ b/lib/screens/settings/components/general/components/language/widget_list_tile_language.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/data/models/supported_locale.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/providers/user_preferences_provider.dart'; -import 'package:lightmeter/screens/settings/components/shared/dialog_picker.dart/widget_dialog_picker.dart'; +import 'package:lightmeter/screens/settings/components/shared/dialog_picker/widget_dialog_picker.dart'; class LanguageListTile extends StatelessWidget { const LanguageListTile({super.key}); diff --git a/lib/screens/settings/components/metering/components/equipment_profiles/widget_list_tile_equipment_profiles.dart b/lib/screens/settings/components/metering/components/equipment_profiles/widget_list_tile_equipment_profiles.dart deleted file mode 100644 index 774e215..0000000 --- a/lib/screens/settings/components/metering/components/equipment_profiles/widget_list_tile_equipment_profiles.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'dart:developer'; - -import 'package:flutter/material.dart'; -import 'package:lightmeter/generated/l10n.dart'; -import 'package:lightmeter/res/dimens.dart'; -import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart'; -import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; -import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; - -class EquipmentProfilesListTile extends StatelessWidget { - const EquipmentProfilesListTile({super.key}); - - @override - Widget build(BuildContext context) { - final paidStatus = IAPProducts.productOf(context, IAPProductType.paidFeatures)?.status ?? - IAPProductStatus.pending; - log(paidStatus.toString()); - return ListTile( - leading: const Icon(Icons.camera), - title: Text(S.of(context).equipmentProfiles), - onTap: switch (paidStatus) { - IAPProductStatus.purchased => () { - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => const EquipmentProfilesScreen()), - ); - }, - IAPProductStatus.pending => null, - _ => () { - IAPProductsProvider.of(context).buy(IAPProductType.paidFeatures); - }, - }, - trailing: switch (paidStatus) { - IAPProductStatus.purchasable => const Icon(Icons.lock), - IAPProductStatus.pending => const SizedBox( - height: Dimens.grid24, - width: Dimens.grid24, - child: CircularProgressIndicator(), - ), - _ => null, - }, - ); - } -} diff --git a/lib/screens/settings/components/metering/components/fractional_stops/widget_list_tile_fractional_stops.dart b/lib/screens/settings/components/metering/components/fractional_stops/widget_list_tile_fractional_stops.dart index 1bcf6bc..e8b99d1 100644 --- a/lib/screens/settings/components/metering/components/fractional_stops/widget_list_tile_fractional_stops.dart +++ b/lib/screens/settings/components/metering/components/fractional_stops/widget_list_tile_fractional_stops.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/providers/user_preferences_provider.dart'; -import 'package:lightmeter/screens/settings/components/shared/dialog_picker.dart/widget_dialog_picker.dart'; +import 'package:lightmeter/screens/settings/components/shared/dialog_picker/widget_dialog_picker.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class StopTypeListTile extends StatelessWidget { diff --git a/lib/screens/settings/components/metering/widget_settings_section_metering.dart b/lib/screens/settings/components/metering/widget_settings_section_metering.dart index 9f86709..c5b9e1d 100644 --- a/lib/screens/settings/components/metering/widget_settings_section_metering.dart +++ b/lib/screens/settings/components/metering/widget_settings_section_metering.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/screens/settings/components/metering/components/calibration/widget_list_tile_calibration.dart'; -import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/widget_list_tile_equipment_profiles.dart'; import 'package:lightmeter/screens/settings/components/metering/components/fractional_stops/widget_list_tile_fractional_stops.dart'; import 'package:lightmeter/screens/settings/components/metering/components/metering_screen_layout/widget_list_tile_metering_screen_layout.dart'; import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart'; @@ -17,7 +16,6 @@ class MeteringSettingsSection extends StatelessWidget { StopTypeListTile(), CalibrationListTile(), MeteringScreenLayoutListTile(), - EquipmentProfilesListTile(), ], ); } diff --git a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/components/dialog_filter/widget_dialog_filter.dart b/lib/screens/settings/components/shared/dialog_filter/widget_dialog_filter.dart similarity index 92% rename from lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/components/dialog_filter/widget_dialog_filter.dart rename to lib/screens/settings/components/shared/dialog_filter/widget_dialog_filter.dart index d91752e..8bafcae 100644 --- a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/components/dialog_filter/widget_dialog_filter.dart +++ b/lib/screens/settings/components/shared/dialog_filter/widget_dialog_filter.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/res/dimens.dart'; -import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; -class DialogFilter extends StatefulWidget { +class DialogFilter extends StatefulWidget { final Icon icon; final String title; final String description; @@ -25,10 +24,10 @@ class DialogFilter extends StatefulWidget { State> createState() => _DialogFilterState(); } -class _DialogFilterState extends State> { +class _DialogFilterState extends State> { late final List checkboxValues = List.generate( widget.values.length, - (index) => widget.selectedValues.any((element) => element.value == widget.values[index].value), + (index) => widget.selectedValues.any((element) => element == widget.values[index]), growable: false, ); diff --git a/lib/screens/settings/components/shared/dialog_picker.dart/widget_dialog_picker.dart b/lib/screens/settings/components/shared/dialog_picker/widget_dialog_picker.dart similarity index 100% rename from lib/screens/settings/components/shared/dialog_picker.dart/widget_dialog_picker.dart rename to lib/screens/settings/components/shared/dialog_picker/widget_dialog_picker.dart diff --git a/lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/components/dialog_range_picker/widget_dialog_picker_range.dart b/lib/screens/settings/components/shared/dialog_range_picker/widget_dialog_picker_range.dart similarity index 100% rename from lib/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/components/dialog_range_picker/widget_dialog_picker_range.dart rename to lib/screens/settings/components/shared/dialog_range_picker/widget_dialog_picker_range.dart diff --git a/lib/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart b/lib/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart new file mode 100644 index 0000000..8c79f37 --- /dev/null +++ b/lib/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; + +/// Depends on the product status and replaces [onTap] with purchase callback +/// if the product is purchasable. +class IAPListTile extends StatelessWidget { + final IAPProductType product; + final Icon leading; + final Text title; + final VoidCallback onTap; + + const IAPListTile({ + this.product = IAPProductType.paidFeatures, + required this.leading, + required this.title, + required this.onTap, + super.key, + }); + + @override + Widget build(BuildContext context) { + return ListTile( + leading: leading, + title: title, + onTap: switch (IAPProducts.productOf(context, product)?.status) { + IAPProductStatus.purchasable => () => IAPProductsProvider.of(context).buy(product), + IAPProductStatus.pending => null, + IAPProductStatus.purchased => onTap, + null => null, + }, + ); + } +} diff --git a/lib/screens/settings/components/shared/settings_section/widget_settings_section.dart b/lib/screens/settings/components/shared/settings_section/widget_settings_section.dart index be62847..9f88d7d 100644 --- a/lib/screens/settings/components/shared/settings_section/widget_settings_section.dart +++ b/lib/screens/settings/components/shared/settings_section/widget_settings_section.dart @@ -4,10 +4,12 @@ import 'package:lightmeter/res/dimens.dart'; class SettingsSection extends StatelessWidget { final String title; final List children; + final bool enabled; const SettingsSection({ required this.title, required this.children, + this.enabled = true, super.key, }); @@ -23,22 +25,25 @@ class SettingsSection extends StatelessWidget { child: Card( child: Padding( padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM), - child: Text( - title, - style: Theme.of(context) - .textTheme - .labelLarge - ?.copyWith(color: Theme.of(context).colorScheme.onSurface), + child: Opacity( + opacity: enabled ? Dimens.enabledOpacity : Dimens.disabledOpacity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM), + child: Text( + title, + style: Theme.of(context) + .textTheme + .labelLarge + ?.copyWith(color: Theme.of(context).colorScheme.onSurface), + ), ), - ), - ...children, - ], + ...children, + ], + ), ), ), ), diff --git a/lib/screens/settings/components/theme/components/theme_type/widget_list_tile_theme_type.dart b/lib/screens/settings/components/theme/components/theme_type/widget_list_tile_theme_type.dart index 8a95161..cfcd694 100644 --- a/lib/screens/settings/components/theme/components/theme_type/widget_list_tile_theme_type.dart +++ b/lib/screens/settings/components/theme/components/theme_type/widget_list_tile_theme_type.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/data/models/theme_type.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/providers/user_preferences_provider.dart'; -import 'package:lightmeter/screens/settings/components/shared/dialog_picker.dart/widget_dialog_picker.dart'; +import 'package:lightmeter/screens/settings/components/shared/dialog_picker/widget_dialog_picker.dart'; class ThemeTypeListTile extends StatelessWidget { const ThemeTypeListTile({super.key}); diff --git a/lib/screens/settings/screen_settings.dart b/lib/screens/settings/screen_settings.dart index 38256e8..7d057cf 100644 --- a/lib/screens/settings/screen_settings.dart +++ b/lib/screens/settings/screen_settings.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/screens/settings/components/about/widget_settings_section_about.dart'; +import 'package:lightmeter/screens/settings/components/equipment/widget_settings_section_equipment.dart'; import 'package:lightmeter/screens/settings/components/general/widget_settings_section_general.dart'; import 'package:lightmeter/screens/settings/components/metering/widget_settings_section_metering.dart'; import 'package:lightmeter/screens/settings/components/theme/widget_settings_section_theme.dart'; @@ -43,6 +44,7 @@ class _SettingsScreenState extends State { delegate: SliverChildListDelegate( [ const MeteringSettingsSection(), + const EquipmentSettingsSection(), const GeneralSettingsSection(), const ThemeSettingsSection(), const AboutSettingsSection(), diff --git a/m3_lightmeter.code-workspace b/m3_lightmeter.code-workspace new file mode 100644 index 0000000..35520eb --- /dev/null +++ b/m3_lightmeter.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": "iap" + }, + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/test/data/models/film_test.dart b/test/data/models/film_test.dart deleted file mode 100644 index 2feb690..0000000 --- a/test/data/models/film_test.dart +++ /dev/null @@ -1,121 +0,0 @@ -import 'package:lightmeter/data/models/film.dart'; -import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; -import 'package:test/test.dart'; - -void main() { - test('iso', () { - expect(const Film.other().iso, 0); - expect(const FomapanFilm.creative100().iso, 100); - expect(const FomapanFilm.creative200().iso, 200); - expect(const FomapanFilm.action400().iso, 400); - expect(const IlfordFilm.ortho().iso, 80); - expect(const IlfordFilm.delta100().iso, 100); - expect(const IlfordFilm.delta400().iso, 400); - expect(const IlfordFilm.delta3200().iso, 3200); - expect(const IlfordFilm.fp4().iso, 125); - expect(const IlfordFilm.hp5().iso, 400); - expect(const IlfordFilm.panf().iso, 50); - expect(const IlfordFilm.sfx200().iso, 200); - expect(const IlfordFilm.xp2super().iso, 400); - expect(const IlfordFilm.pan100().iso, 100); - expect(const IlfordFilm.pan400().iso, 400); - expect(const KodakFilm.tmax100().iso, 100); - expect(const KodakFilm.tmax400().iso, 400); - expect(const KodakFilm.tmax3200().iso, 3200); - expect(const KodakFilm.trix320().iso, 320); - expect(const KodakFilm.trix400().iso, 400); - }); - - test('toString()', () { - expect(const Film.other().toString(), ""); - expect(const FomapanFilm.creative100().toString(), "Fomapan CREATIVE 100"); - expect(const FomapanFilm.creative200().toString(), "Fomapan CREATIVE 200"); - expect(const FomapanFilm.action400().toString(), "Fomapan ACTION 400"); - expect(const IlfordFilm.ortho().toString(), "Ilford ORTHO+"); - expect(const IlfordFilm.delta100().toString(), "Ilford DELTA 100"); - expect(const IlfordFilm.delta400().toString(), "Ilford DELTA 400"); - expect(const IlfordFilm.delta3200().toString(), "Ilford DELTA 3200"); - expect(const IlfordFilm.fp4().toString(), "Ilford FP4+"); - expect(const IlfordFilm.hp5().toString(), "Ilford HP5+"); - expect(const IlfordFilm.panf().toString(), "Ilford Pan F+"); - expect(const IlfordFilm.sfx200().toString(), "Ilford SFX 200"); - expect(const IlfordFilm.xp2super().toString(), "Ilford XP2 SUPER"); - expect(const IlfordFilm.pan100().toString(), "Kentemere 100"); - expect(const IlfordFilm.pan400().toString(), "Kentemere 400"); - expect(const KodakFilm.tmax100().toString(), "Kodak T-MAX 100"); - expect(const KodakFilm.tmax400().toString(), "Kodak T-MAX 400"); - expect(const KodakFilm.tmax3200().toString(), "Kodak T-MAX 3200"); - expect(const KodakFilm.trix320().toString(), "Kodak TRI-X 320"); - expect(const KodakFilm.trix400().toString(), "Kodak TRI-X 400"); - }); - - group( - 'reciprocityFailure', - () { - const inputSpeeds = [ - ShutterSpeedValue(1000, true, StopType.full), - ShutterSpeedValue(1, false, StopType.full), - ShutterSpeedValue(16, false, StopType.full) - ]; - test('No change `Film.other()`', () { - expect( - const Film.other().reciprocityFailure(inputSpeeds[0]), - const ShutterSpeedValue(1000, true, StopType.full), - ); - expect( - const Film.other().reciprocityFailure(inputSpeeds[1]), - const ShutterSpeedValue(1, false, StopType.full), - ); - expect( - const Film.other().reciprocityFailure(inputSpeeds[2]), - const ShutterSpeedValue(16, false, StopType.full), - ); - }); - - test('pow `IlfordFilm.delta100()`', () { - expect( - const IlfordFilm.delta100().reciprocityFailure(inputSpeeds[0]), - const ShutterSpeedValue(1000, true, StopType.full), - ); - expect( - const IlfordFilm.delta100().reciprocityFailure(inputSpeeds[1]), - const ShutterSpeedValue(1, false, StopType.full), - ); - expect( - const IlfordFilm.delta100().reciprocityFailure(inputSpeeds[2]), - const ShutterSpeedValue(32.899642452994128, false, StopType.full), - ); - }); - - test('log10polynomian `FomapanFilm.creative100()`', () { - expect( - const FomapanFilm.creative100().reciprocityFailure(inputSpeeds[0]), - const ShutterSpeedValue(1000, true, StopType.full), - ); - expect( - const FomapanFilm.creative100().reciprocityFailure(inputSpeeds[1]), - const ShutterSpeedValue(2, false, StopType.full), - ); - expect( - const FomapanFilm.creative100().reciprocityFailure(inputSpeeds[2]), - const ShutterSpeedValue(151.52807753457483, false, StopType.full), - ); - }); - - test('log10polynomian `Kodak.tmax400()`', () { - expect( - const KodakFilm.tmax400().reciprocityFailure(inputSpeeds[0]), - const ShutterSpeedValue(1000, true, StopType.full), - ); - expect( - const KodakFilm.tmax400().reciprocityFailure(inputSpeeds[1]), - const ShutterSpeedValue(1.3333333333333333, false, StopType.full), - ); - expect( - const KodakFilm.tmax400().reciprocityFailure(inputSpeeds[2]), - const ShutterSpeedValue(27.166026086819844, false, StopType.full), - ); - }); - }, - ); -} diff --git a/test/data/shared_prefs_service_test.dart b/test/data/shared_prefs_service_test.dart index 2a63fe5..0896626 100644 --- a/test/data/shared_prefs_service_test.dart +++ b/test/data/shared_prefs_service_test.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:lightmeter/data/models/ev_source_type.dart'; -import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/data/models/metering_screen_layout_config.dart'; import 'package:lightmeter/data/models/supported_locale.dart'; import 'package:lightmeter/data/models/theme_type.dart'; @@ -392,26 +391,4 @@ void main() { .called(1); }); }); - - group('film', () { - test('get default', () { - when(() => sharedPreferences.getString(UserPreferencesService.filmKey)).thenReturn(null); - expect(service.film, Film.values.first); - }); - - test('get', () { - when(() => sharedPreferences.getString(UserPreferencesService.filmKey)) - .thenReturn('Fomapan ACTION 400'); - expect(service.film, const FomapanFilm.action400()); - }); - - test('set', () { - when(() => sharedPreferences.setString(UserPreferencesService.filmKey, 'Fomapan ACTION 400')) - .thenAnswer((_) => Future.value(true)); - service.film = const FomapanFilm.action400(); - verify( - () => sharedPreferences.setString(UserPreferencesService.filmKey, 'Fomapan ACTION 400'), - ).called(1); - }); - }); } diff --git a/test/interactors/metering_interactor_test.dart b/test/interactors/metering_interactor_test.dart index bd6a169..1f0b05a 100644 --- a/test/interactors/metering_interactor_test.dart +++ b/test/interactors/metering_interactor_test.dart @@ -2,7 +2,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:lightmeter/data/caffeine_service.dart'; import 'package:lightmeter/data/haptics_service.dart'; import 'package:lightmeter/data/light_sensor_service.dart'; -import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/data/models/volume_action.dart'; import 'package:lightmeter/data/permissions_service.dart'; import 'package:lightmeter/data/shared_prefs_service.dart'; @@ -124,19 +123,6 @@ void main() { interactor.ndFilter = NdValue.values.first; verify(() => mockUserPreferencesService.ndFilter = NdValue.values.first).called(1); }); - - test('film - get', () async { - when(() => mockUserPreferencesService.film).thenReturn(Film.values.first); - expect(interactor.film, Film.values.first); - verify(() => mockUserPreferencesService.film).called(1); - }); - - test('film - set', () async { - when(() => mockUserPreferencesService.film = Film.values.first) - .thenReturn(Film.values.first); - interactor.film = Film.values.first; - verify(() => mockUserPreferencesService.film = Film.values.first).called(1); - }); }, ); diff --git a/test/screens/metering/bloc_metering_test.dart b/test/screens/metering/bloc_metering_test.dart index 45b9291..fbffbbc 100644 --- a/test/screens/metering/bloc_metering_test.dart +++ b/test/screens/metering/bloc_metering_test.dart @@ -1,5 +1,4 @@ import 'package:bloc_test/bloc_test.dart'; -import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/data/models/volume_action.dart'; import 'package:lightmeter/interactors/metering_interactor.dart'; import 'package:lightmeter/screens/metering/bloc_metering.dart'; @@ -34,7 +33,6 @@ void main() { meteringInteractor = _MockMeteringInteractor(); when(() => meteringInteractor.iso).thenReturn(iso100); when(() => meteringInteractor.ndFilter).thenReturn(NdValue.values.first); - when(() => meteringInteractor.film).thenReturn(Film.values.first); when(meteringInteractor.quickVibration).thenAnswer((_) async {}); when(meteringInteractor.responseVibration).thenAnswer((_) async {}); when(meteringInteractor.errorVibration).thenAnswer((_) async {}); @@ -157,7 +155,6 @@ void main() { build: () => bloc, seed: () => MeteringDataState( ev100: 1.0, - film: Film.values[1], iso: const IsoValue(100, StopType.full), nd: NdValue.values.first, isMetering: false, @@ -166,14 +163,12 @@ void main() { bloc.add(const IsoChangedEvent(IsoValue(200, StopType.full))); }, verify: (_) { - verify(() => meteringInteractor.film = Film.values.first).called(1); verify(() => meteringInteractor.iso = const IsoValue(200, StopType.full)).called(1); }, expect: () => [ isA() .having((state) => state.ev100, 'ev100', 1.0) .having((state) => state.ev, 'ev', 2.0) - .having((state) => state.film, 'film', Film.values.first) .having((state) => state.iso, 'iso', const IsoValue(200, StopType.full)) .having((state) => state.nd, 'nd', NdValue.values.first) .having((state) => state.isMetering, 'isMetering', false), @@ -185,7 +180,6 @@ void main() { build: () => bloc, seed: () => MeteringDataState( ev100: null, - film: Film.values[1], iso: const IsoValue(100, StopType.full), nd: NdValue.values.first, isMetering: false, @@ -194,14 +188,12 @@ void main() { bloc.add(const IsoChangedEvent(IsoValue(200, StopType.full))); }, verify: (_) { - verify(() => meteringInteractor.film = Film.values.first).called(1); verify(() => meteringInteractor.iso = const IsoValue(200, StopType.full)).called(1); }, expect: () => [ isA() .having((state) => state.ev100, 'ev100', null) .having((state) => state.ev, 'ev', null) - .having((state) => state.film, 'film', Film.values.first) .having((state) => state.iso, 'iso', const IsoValue(200, StopType.full)) .having((state) => state.nd, 'nd', NdValue.values.first) .having((state) => state.isMetering, 'isMetering', false), @@ -213,7 +205,6 @@ void main() { build: () => bloc, seed: () => MeteringDataState( ev100: 1.0, - film: Film.values[1], iso: const IsoValue(100, StopType.full), nd: NdValue.values.first, isMetering: false, @@ -222,7 +213,6 @@ void main() { bloc.add(const IsoChangedEvent(IsoValue(100, StopType.full))); }, verify: (_) { - verify(() => meteringInteractor.film = Film.values.first).called(1); verifyNever(() => meteringInteractor.iso = const IsoValue(100, StopType.full)); }, expect: () => [], @@ -233,7 +223,6 @@ void main() { build: () => bloc, seed: () => MeteringDataState( ev100: 1.0, - film: Film.values[1], iso: const IsoValue(100, StopType.full), nd: NdValue.values.first, isMetering: false, @@ -244,14 +233,12 @@ void main() { bloc.onCommunicationState(const communication_states.MeteringEndedState(2)); }, verify: (_) { - verify(() => meteringInteractor.film = Film.values.first).called(1); verify(() => meteringInteractor.iso = const IsoValue(200, StopType.full)).called(1); }, expect: () => [ isA() .having((state) => state.ev100, 'ev100', 1.0) .having((state) => state.ev, 'ev', 2.0) - .having((state) => state.film, 'film', Film.values.first) .having((state) => state.iso, 'iso', const IsoValue(200, StopType.full)) .having((state) => state.nd, 'nd', NdValue.values.first) .having((state) => state.isMetering, 'isMetering', false), @@ -259,7 +246,6 @@ void main() { isA() .having((state) => state.ev100, 'ev100', 2.0) .having((state) => state.ev, 'ev', 3.0) - .having((state) => state.film, 'film', Film.values.first) .having((state) => state.iso, 'iso', const IsoValue(200, StopType.full)) .having((state) => state.nd, 'nd', NdValue.values.first) .having((state) => state.isMetering, 'isMetering', false), @@ -276,7 +262,6 @@ void main() { build: () => bloc, seed: () => MeteringDataState( ev100: 1.0, - film: Film.values[1], iso: const IsoValue(100, StopType.full), nd: NdValue.values.first, isMetering: false, @@ -291,7 +276,6 @@ void main() { isA() .having((state) => state.ev100, 'ev100', 1.0) .having((state) => state.ev, 'ev', 0.0) - .having((state) => state.film, 'film', Film.values[1]) .having((state) => state.iso, 'iso', const IsoValue(100, StopType.full)) .having((state) => state.nd, 'nd', const NdValue(2)) .having((state) => state.isMetering, 'isMetering', false), @@ -303,7 +287,6 @@ void main() { build: () => bloc, seed: () => MeteringDataState( ev100: null, - film: Film.values[1], iso: const IsoValue(100, StopType.full), nd: NdValue.values.first, isMetering: false, @@ -318,7 +301,6 @@ void main() { isA() .having((state) => state.ev100, 'ev100', null) .having((state) => state.ev, 'ev', null) - .having((state) => state.film, 'film', Film.values[1]) .having((state) => state.iso, 'iso', const IsoValue(100, StopType.full)) .having((state) => state.nd, 'nd', const NdValue(2)) .having((state) => state.isMetering, 'isMetering', false), @@ -330,7 +312,6 @@ void main() { build: () => bloc, seed: () => MeteringDataState( ev100: 1.0, - film: Film.values[1], iso: const IsoValue(100, StopType.full), nd: NdValue.values.first, isMetering: false, @@ -349,7 +330,6 @@ void main() { build: () => bloc, seed: () => MeteringDataState( ev100: 1.0, - film: Film.values[1], iso: const IsoValue(100, StopType.full), nd: NdValue.values.first, isMetering: false, @@ -366,7 +346,6 @@ void main() { isA() .having((state) => state.ev100, 'ev100', 1.0) .having((state) => state.ev, 'ev', 0.0) - .having((state) => state.film, 'film', Film.values[1]) .having((state) => state.iso, 'iso', const IsoValue(100, StopType.full)) .having((state) => state.nd, 'nd', const NdValue(2)) .having((state) => state.isMetering, 'isMetering', false), @@ -374,7 +353,6 @@ void main() { isA() .having((state) => state.ev100, 'ev100', 2.0) .having((state) => state.ev, 'ev', 1.0) - .having((state) => state.film, 'film', Film.values[1]) .having((state) => state.iso, 'iso', const IsoValue(100, StopType.full)) .having((state) => state.nd, 'nd', const NdValue(2)) .having((state) => state.isMetering, 'isMetering', false), @@ -383,115 +361,6 @@ void main() { }, ); - group( - '`FilmChangedEvent`', - () { - blocTest( - 'Pick different film with different ISO', - build: () => bloc, - seed: () => MeteringDataState( - ev100: 1.0, - film: const FomapanFilm.creative100(), - iso: const IsoValue(100, StopType.full), - nd: NdValue.values.first, - isMetering: false, - ), - act: (bloc) async { - bloc.add(const FilmChangedEvent(FomapanFilm.creative200())); - }, - verify: (_) { - verify(() => meteringInteractor.film = const FomapanFilm.creative200()).called(1); - verify(() => meteringInteractor.iso = const IsoValue(200, StopType.full)).called(1); - }, - expect: () => [ - isA() - .having((state) => state.ev100, 'ev100', 1.0) - .having((state) => state.ev, 'ev', 2.0) - .having((state) => state.film, 'film', const FomapanFilm.creative200()) - .having((state) => state.iso, 'iso', const IsoValue(200, StopType.full)) - .having((state) => state.nd, 'nd', NdValue.values.first) - .having((state) => state.isMetering, 'isMetering', false), - ], - ); - - blocTest( - 'Pick different film with same ISO', - build: () => bloc, - seed: () => MeteringDataState( - ev100: 1.0, - film: const FomapanFilm.creative100(), - iso: const IsoValue(100, StopType.full), - nd: NdValue.values.first, - isMetering: false, - ), - act: (bloc) async { - bloc.add(const FilmChangedEvent(IlfordFilm.delta100())); - }, - verify: (_) { - verify(() => meteringInteractor.film = const IlfordFilm.delta100()).called(1); - verifyNever(() => meteringInteractor.iso = const IsoValue(100, StopType.full)); - }, - expect: () => [ - isA() - .having((state) => state.ev100, 'ev100', 1.0) - .having((state) => state.ev, 'ev', 1.0) - .having((state) => state.film, 'film', const IlfordFilm.delta100()) - .having((state) => state.iso, 'iso', const IsoValue(100, StopType.full)) - .having((state) => state.nd, 'nd', NdValue.values.first) - .having((state) => state.isMetering, 'isMetering', false), - ], - ); - - blocTest( - 'Pick same film', - build: () => bloc, - seed: () => MeteringDataState( - ev100: 1.0, - film: const FomapanFilm.creative100(), - iso: const IsoValue(100, StopType.full), - nd: NdValue.values.first, - isMetering: false, - ), - act: (bloc) async { - bloc.add(const FilmChangedEvent(FomapanFilm.creative100())); - }, - verify: (_) { - verifyNever(() => meteringInteractor.film = const FomapanFilm.creative100()); - }, - expect: () => [], - ); - - blocTest( - 'Pick `Film.other()`', - build: () => bloc, - seed: () => MeteringDataState( - ev100: 1.0, - film: const FomapanFilm.creative100(), - iso: const IsoValue(100, StopType.full), - nd: NdValue.values.first, - isMetering: false, - ), - act: (bloc) async { - bloc.add(const FilmChangedEvent(Film.other())); - }, - verify: (_) { - verify(() => meteringInteractor.film = const Film.other()).called(1); - verifyNever(() => meteringInteractor.iso = const IsoValue(0, StopType.full)); - verifyNever(() => meteringInteractor.responseVibration()); - }, - expect: () => [ - isA() - .having((state) => state.ev100, 'ev100', 1.0) - .having((state) => state.ev, 'ev', 1.0) - .having((state) => state.film, 'film', const Film.other()) - .having((state) => state.iso, 'iso', const IsoValue(100, StopType.full)) - .having((state) => state.nd, 'nd', NdValue.values.first) - .having((state) => state.isMetering, 'isMetering', false), - ], - ); - }, - ); - group( '`EquipmentProfileChangedEvent`', () { @@ -509,7 +378,6 @@ void main() { build: () => bloc, seed: () => MeteringDataState( ev100: 1.0, - film: Film.values[1], iso: const IsoValue(100, StopType.full), nd: NdValue.values.first, isMetering: false, @@ -518,7 +386,6 @@ void main() { bloc.add(EquipmentProfileChangedEvent(reducedProfile)); }, verify: (_) { - verifyNever(() => meteringInteractor.film = const Film.other()); verifyNever(() => meteringInteractor.iso = reducedProfile.isoValues.first); verifyNever(() => meteringInteractor.ndFilter = reducedProfile.ndValues.first); verifyNever(() => meteringInteractor.responseVibration()); @@ -531,7 +398,6 @@ void main() { build: () => bloc, seed: () => MeteringDataState( ev100: 1.0, - film: Film.values[1], iso: IsoValue.values[2], nd: NdValue.values.first, isMetering: false, @@ -540,7 +406,6 @@ void main() { bloc.add(EquipmentProfileChangedEvent(reducedProfile)); }, verify: (_) { - verify(() => meteringInteractor.film = const Film.other()).called(1); verify(() => meteringInteractor.iso = reducedProfile.isoValues.first).called(1); verifyNever(() => meteringInteractor.ndFilter = reducedProfile.ndValues.first); verify(() => meteringInteractor.responseVibration()).called(1); @@ -548,7 +413,6 @@ void main() { expect: () => [ isA() .having((state) => state.ev100, 'ev100', 1.0) - .having((state) => state.film, 'film', const Film.other()) .having((state) => state.iso, 'iso', reducedProfile.isoValues.first) .having((state) => state.nd, 'nd', NdValue.values.first) .having((state) => state.isMetering, 'isMetering', false), @@ -560,7 +424,6 @@ void main() { build: () => bloc, seed: () => MeteringDataState( ev100: 1.0, - film: Film.values[1], iso: const IsoValue(100, StopType.full), nd: NdValue.values[4], isMetering: false, @@ -569,7 +432,6 @@ void main() { bloc.add(EquipmentProfileChangedEvent(reducedProfile)); }, verify: (_) { - verifyNever(() => meteringInteractor.film = const Film.other()); verifyNever(() => meteringInteractor.iso = reducedProfile.isoValues.first); verify(() => meteringInteractor.ndFilter = reducedProfile.ndValues.first).called(1); verify(() => meteringInteractor.responseVibration()).called(1); @@ -577,7 +439,6 @@ void main() { expect: () => [ isA() .having((state) => state.ev100, 'ev100', 1.0) - .having((state) => state.film, 'film', Film.values[1]) .having((state) => state.iso, 'iso', const IsoValue(100, StopType.full)) .having((state) => state.nd, 'nd', reducedProfile.ndValues.first) .having((state) => state.isMetering, 'isMetering', false), @@ -589,7 +450,6 @@ void main() { build: () => bloc, seed: () => MeteringDataState( ev100: 1.0, - film: Film.values[1], iso: IsoValue.values[2], nd: NdValue.values[4], isMetering: false, @@ -598,7 +458,6 @@ void main() { bloc.add(EquipmentProfileChangedEvent(reducedProfile)); }, verify: (_) { - verify(() => meteringInteractor.film = const Film.other()).called(1); verify(() => meteringInteractor.iso = reducedProfile.isoValues.first).called(1); verify(() => meteringInteractor.ndFilter = reducedProfile.ndValues.first).called(1); verify(() => meteringInteractor.responseVibration()).called(1); @@ -606,7 +465,6 @@ void main() { expect: () => [ isA() .having((state) => state.ev100, 'ev100', 1.0) - .having((state) => state.film, 'film', const Film.other()) .having((state) => state.iso, 'iso', reducedProfile.isoValues.first) .having((state) => state.nd, 'nd', reducedProfile.ndValues.first) .having((state) => state.isMetering, 'isMetering', false), diff --git a/test/screens/metering/screen_metering_test.dart b/test/screens/metering/screen_metering_test.dart index bb50168..ffcca8d 100644 --- a/test/screens/metering/screen_metering_test.dart +++ b/test/screens/metering/screen_metering_test.dart @@ -1,6 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; -import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/screens/metering/screen_metering.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; @@ -19,7 +18,6 @@ void main() { ev, StopType.full, defaultEquipmentProfile, - const Film.other(), ); test('isNan', () { @@ -42,7 +40,6 @@ void main() { ev, StopType.full, defaultEquipmentProfile, - const Film.other(), ); test('EV 1', () { @@ -142,7 +139,6 @@ void main() { ev, StopType.half, defaultEquipmentProfile, - const Film.other(), ); test('EV 1', () { @@ -242,7 +238,6 @@ void main() { ev, StopType.third, defaultEquipmentProfile, - const Film.other(), ); test('EV 1', () { @@ -356,7 +351,6 @@ void main() { ev, StopType.full, equipmentProfile, - const Film.other(), ); test('EV 1', () { @@ -456,7 +450,6 @@ void main() { ev, StopType.half, equipmentProfile, - const Film.other(), ); test('EV 1', () { @@ -556,7 +549,6 @@ void main() { ev, StopType.third, equipmentProfile, - const Film.other(), ); test('EV 1', () { @@ -669,7 +661,6 @@ void main() { ev, StopType.full, equipmentProfile, - const Film.other(), ); test('EV 1', () { @@ -769,7 +760,6 @@ void main() { ev, StopType.half, equipmentProfile, - const Film.other(), ); test('EV 1', () { @@ -869,7 +859,6 @@ void main() { ev, StopType.third, equipmentProfile, - const Film.other(), ); test('EV 1', () {