From 1310b78a54d85ba031577e8473f5de66098a6565 Mon Sep 17 00:00:00 2001 From: Vadim <44135514+vodemn@users.noreply.github.com> Date: Sat, 5 Aug 2023 21:11:23 +0200 Subject: [PATCH 1/4] ML-61 Delete artefacts after release creation (#96) * Replaced "Build ..." flow with "Create new release" * Renamed other flows --- .../workflows/{cd_dev.yml => build_apk.yml} | 10 +-- .../{cd_prod.yml => create_release.yml} | 76 +++++++++++++++---- .github/workflows/{ci.yml => pr_check.yml} | 0 3 files changed, 62 insertions(+), 24 deletions(-) rename .github/workflows/{cd_dev.yml => build_apk.yml} (91%) rename .github/workflows/{cd_prod.yml => create_release.yml} (72%) rename .github/workflows/{ci.yml => pr_check.yml} (100%) diff --git a/.github/workflows/cd_dev.yml b/.github/workflows/build_apk.yml similarity index 91% rename from .github/workflows/cd_dev.yml rename to .github/workflows/build_apk.yml index e0e6a25..c972954 100644 --- a/.github/workflows/cd_dev.yml +++ b/.github/workflows/build_apk.yml @@ -19,17 +19,11 @@ on: jobs: build: + name: Build .apk runs-on: macos-11 - timeout-minutes: 30 + timeout-minutes: 15 steps: - # - uses: shaunco/ssh-agent@git-repo-mapping - # with: - # ssh-private-key: | - # ${{ secrets.M3_LIGHTMETER_IAP_KEY }} - # repo-mappings: | - # github.com/vodemn/m3_lightmeter_iap - - uses: actions/checkout@v3 with: submodules: recursive diff --git a/.github/workflows/cd_prod.yml b/.github/workflows/create_release.yml similarity index 72% rename from .github/workflows/cd_prod.yml rename to .github/workflows/create_release.yml index 9e45644..6fe5d77 100644 --- a/.github/workflows/cd_prod.yml +++ b/.github/workflows/create_release.yml @@ -3,7 +3,9 @@ # separate terms of service, privacy policy, and support # documentation. -name: Build prod .aab & .apk +name: Create new release + +run-name: Release v${{ inputs.version }} on: workflow_dispatch: @@ -20,15 +22,9 @@ jobs: build: name: Build .apk & .aab runs-on: macos-11 - timeout-minutes: 30 - steps: - # - uses: shaunco/ssh-agent@git-repo-mapping - # with: - # ssh-private-key: | - # ${{ secrets.M3_LIGHTMETER_IAP_KEY }} - # repo-mappings: | - # github.com/vodemn/m3_lightmeter_iap + timeout-minutes: 15 + steps: - uses: actions/checkout@v3 with: submodules: recursive @@ -142,18 +138,66 @@ jobs: with: name: m3_lightmeter_apk + - name: Rename apk + run: mv app-prod-release.apk m3_lightmeter.apk + + - uses: ncipollo/release-action@v1.12.0 + with: + artifacts: "m3_lightmeter.apk" + skipIfReleaseExists: true + tag: "v${{ github.event.inputs.version }}" + + - name: Delete no longer used apk artifact + uses: geekyeggo/delete-artifact@v2 + with: + name: m3_lightmeter_apk + + extract-merged-native-libs: + name: Extract merged native libraries + needs: [build] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + - name: Download app bundle uses: actions/download-artifact@v3 with: name: m3_lightmeter_bundle - - name: Rename artifacts + - name: Extract & zip merged_native_libs run: | - mv app-prod-release.apk m3_lightmeter.apk - mv app-prod-release.aab m3_lightmeter.aab + unzip app-prod-release.aab + (cd base/lib && zip -r "$OLDPWD/merged_native_libs.zip" .) - - uses: ncipollo/release-action@v1.12.0 + - name: Zip app bundle and merged_native_libs + run: zip m3_lightmeter_release.zip app-prod-release.aab merged_native_libs.zip + + - name: Upload merged_native_libs.zip to artifacts + uses: actions/upload-artifact@v3 with: - artifacts: "m3_lightmeter.apk, m3_lightmeter.aab" - skipIfReleaseExists: true - tag: "v${{ github.event.inputs.version }}" + name: m3_lightmeter_release + path: m3_lightmeter_release.zip + + # TODO: this should be moved to `create-google-play-release` step when it is implemented + - name: Delete no longer used app bundle artifact + uses: geekyeggo/delete-artifact@v2 + with: + name: m3_lightmeter_bundle + + # TODO: Automate Google Play releases creation + create-google-play-release: + if: false + name: Create Google Play release + needs: [build, extract-merged-native-libs] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Download app bundle + uses: actions/download-artifact@v3 + with: + name: m3_lightmeter_bundle diff --git a/.github/workflows/ci.yml b/.github/workflows/pr_check.yml similarity index 100% rename from .github/workflows/ci.yml rename to .github/workflows/pr_check.yml From 886188bb9e7997bbf0595be6a1b3cbef545f278d Mon Sep 17 00:00:00 2001 From: Vadim <44135514+vodemn@users.noreply.github.com> Date: Sun, 6 Aug 2023 16:28:20 +0200 Subject: [PATCH 2/4] ML-95 Live histogram (#97) * Added histogram and separated camera view builder * Added histogram to `MeteringScreenLayoutConfig` * `ResolutionPreset.medium` -> `ResolutionPreset.low` * Adjusted histogram paddings --- .github/workflows/build_apk.yml | 2 +- .github/workflows/create_release.yml | 2 +- .vscode/launch.json | 4 +- .vscode/tasks.json | 6 +- README.md | 4 +- .../models/metering_screen_layout_config.dart | 10 +- lib/data/shared_prefs_service.dart | 1 + lib/l10n/intl_en.arb | 1 + lib/l10n/intl_fr.arb | 1 + lib/l10n/intl_ru.arb | 1 + lib/l10n/intl_zh.arb | 1 + .../bloc_container_camera.dart | 2 +- .../camera_view/widget_camera_view.dart | 10 +- .../widget_placeholder_camera_view.dart | 0 .../histogram/widget_histogram.dart | 132 ++++++++++++++++++ .../camera_preview/widget_camera_preview.dart | 62 ++++++++ .../widget_container_camera.dart | 26 ++-- ...ialog_metering_screen_layout_features.dart | 2 + .../metering_screen_layout_config_test.dart | 60 +++++--- test/data/shared_prefs_service_test.dart | 7 +- 20 files changed, 280 insertions(+), 54 deletions(-) rename lib/screens/metering/components/camera_container/components/{ => camera_preview/components}/camera_view/widget_camera_view.dart (86%) rename lib/screens/metering/components/camera_container/components/{ => camera_preview/components}/camera_view_placeholder/widget_placeholder_camera_view.dart (100%) create mode 100644 lib/screens/metering/components/camera_container/components/camera_preview/components/histogram/widget_histogram.dart create mode 100644 lib/screens/metering/components/camera_container/components/camera_preview/widget_camera_preview.dart diff --git a/.github/workflows/build_apk.yml b/.github/workflows/build_apk.yml index c972954..9157f90 100644 --- a/.github/workflows/build_apk.yml +++ b/.github/workflows/build_apk.yml @@ -76,7 +76,7 @@ jobs: - name: Build Apk env: FLAVOR: ${{ github.event.inputs.flavor }} - run: flutter build apk --release --flavor $FLAVOR --dart-define cameraPreviewAspectRatio=2/3 -t lib/main_$FLAVOR.dart + run: flutter build apk --release --flavor $FLAVOR --dart-define c -t lib/main_$FLAVOR.dart - name: Upload artifact uses: actions/upload-artifact@v3 diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml index 6fe5d77..3851170 100644 --- a/.github/workflows/create_release.yml +++ b/.github/workflows/create_release.yml @@ -16,7 +16,7 @@ on: type: string env: - BUILD_ARGS: --release --flavor prod --dart-define cameraPreviewAspectRatio=2/3 -t lib/main_prod.dart + BUILD_ARGS: --release --flavor prod --dart-define cameraPreviewAspectRatio=240/320 -t lib/main_prod.dart jobs: build: diff --git a/.vscode/launch.json b/.vscode/launch.json index fb960be..822d34d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,7 +12,7 @@ "--flavor", "dev", "--dart-define", - "cameraPreviewAspectRatio=2/3", + "cameraPreviewAspectRatio=240/320", ], "program": "${workspaceFolder}/lib/main_dev.dart", }, @@ -36,7 +36,7 @@ "--flavor", "prod", "--dart-define", - "cameraPreviewAspectRatio=2/3", + "cameraPreviewAspectRatio=240/320", ], "program": "${workspaceFolder}/lib/main_prod.dart", }, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 8615437..adaac0f 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -12,7 +12,7 @@ "dev", "--release", "--dart-define", - "cameraPreviewAspectRatio=2/3", + "cameraPreviewAspectRatio=240/320", "-t", "lib/main_dev.dart", ], @@ -28,7 +28,7 @@ "prod", "--release", "--dart-define", - "cameraPreviewAspectRatio=2/3", + "cameraPreviewAspectRatio=240/320", "-t", "lib/main_prod.dart", ], @@ -44,7 +44,7 @@ "prod", "--release", "--dart-define", - "cameraPreviewAspectRatio=2/3", + "cameraPreviewAspectRatio=240/320", "-t", "lib/main_prod.dart", ], diff --git a/README.md b/README.md index 2fef498..f629aa2 100644 --- a/README.md +++ b/README.md @@ -45,11 +45,11 @@ flutter pub get flutter pub run intl_utils:generate ``` -### 4. Build +### 4. Build (Android) You can build an apk by running the following command from the root of the repository: ```console -flutter build apk --release --flavor $FLAVOR --dart-define cameraPreviewAspectRatio=2/3 -t lib/main_$FLAVOR.dart +flutter build apk --release --flavor $FLAVOR --dart-define cameraPreviewAspectRatio=240/320 -t lib/main_$FLAVOR.dart ``` Just replace `$FLAVOR` with `dev` or `prod`. diff --git a/lib/data/models/metering_screen_layout_config.dart b/lib/data/models/metering_screen_layout_config.dart index 3410632..0aae055 100644 --- a/lib/data/models/metering_screen_layout_config.dart +++ b/lib/data/models/metering_screen_layout_config.dart @@ -1,11 +1,13 @@ -enum MeteringScreenLayoutFeature { extremeExposurePairs, filmPicker } +enum MeteringScreenLayoutFeature { extremeExposurePairs, filmPicker, histogram } typedef MeteringScreenLayoutConfig = Map; extension MeteringScreenLayoutConfigJson on MeteringScreenLayoutConfig { - static MeteringScreenLayoutConfig fromJson(Map data) => data.map( - (key, value) => MapEntry(MeteringScreenLayoutFeature.values[int.parse(key)], value as bool), - ); + static MeteringScreenLayoutConfig fromJson(Map data) => + { + for (final f in MeteringScreenLayoutFeature.values) + f: data[f.index.toString()] as bool? ?? true + }; Map toJson() => map((key, value) => MapEntry(key.index.toString(), value)); } diff --git a/lib/data/shared_prefs_service.dart b/lib/data/shared_prefs_service.dart index 8105d5c..8ae5487 100644 --- a/lib/data/shared_prefs_service.dart +++ b/lib/data/shared_prefs_service.dart @@ -97,6 +97,7 @@ class UserPreferencesService { return { MeteringScreenLayoutFeature.extremeExposurePairs: true, MeteringScreenLayoutFeature.filmPicker: true, + MeteringScreenLayoutFeature.histogram: true, }; } } diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 39700b8..1bfe8ed 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -38,6 +38,7 @@ "meteringScreenLayoutHint": "Hide elements on the metering screen that you don't need so that they don't waste exposure pairs list space.", "meteringScreenFeatureExtremeExposurePairs": "Fastest & shortest exposure pairs", "meteringScreenFeatureFilmPicker": "Film picker", + "meteringScreenFeatureHistogram": "Histogram", "film": "Film", "equipment": "Equipment", "equipmentProfileName": "Equipment profile name", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 5957d0d..f75d76b 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -38,6 +38,7 @@ "meteringScreenLayoutHint": "Masquer les éléments sur l'écran de mesure dont vous n'avez pas besoin pour qu'ils ne gaspillent pas de l'espace dans les paires d'exposition.", "meteringScreenFeatureExtremeExposurePairs": "Paires d'exposition les plus rapides et les plus courtes", "meteringScreenFeatureFilmPicker": "Sélecteur de film", + "meteringScreenFeatureHistogram": "Histogramme", "film": "Pellicule", "equipment": "Équipement", "equipmentProfileName": "Nom du profil de l'équipement", diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index f6b3833..3a67fc2 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -38,6 +38,7 @@ "meteringScreenLayoutHint": "Здесь вы можете скрыть некоторые ненужные или неиспользуемые элементы с главного экрана.", "meteringScreenFeatureExtremeExposurePairs": "Длинная и короткая выдержки", "meteringScreenFeatureFilmPicker": "Выбор пленки", + "meteringScreenFeatureHistogram": "Гистограмма", "film": "Пленка", "equipment": "Оборудование", "equipmentProfileName": "Название профиля", diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index b931906..5d020cf 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -38,6 +38,7 @@ "meteringScreenLayoutHint": "隐藏不需要的元素,以免浪费曝光列表空间", "meteringScreenFeatureExtremeExposurePairs": "最快 & 最慢曝光组合", "meteringScreenFeatureFilmPicker": "胶片选择", + "meteringScreenFeatureHistogram": "直方图", "film": "胶片", "equipment": "设备", "equipmentProfileName": "设备配置名称", diff --git a/lib/screens/metering/components/camera_container/bloc_container_camera.dart b/lib/screens/metering/components/camera_container/bloc_container_camera.dart index 9c8658d..991bdf6 100644 --- a/lib/screens/metering/components/camera_container/bloc_container_camera.dart +++ b/lib/screens/metering/components/camera_container/bloc_container_camera.dart @@ -123,7 +123,7 @@ class CameraContainerBloc extends EvSourceBlocBase camera.lensDirection == CameraLensDirection.back, orElse: () => cameras.last, ), - ResolutionPreset.medium, + ResolutionPreset.low, enableAudio: false, ); diff --git a/lib/screens/metering/components/camera_container/components/camera_view/widget_camera_view.dart b/lib/screens/metering/components/camera_container/components/camera_preview/components/camera_view/widget_camera_view.dart similarity index 86% rename from lib/screens/metering/components/camera_container/components/camera_view/widget_camera_view.dart rename to lib/screens/metering/components/camera_container/components/camera_preview/components/camera_view/widget_camera_view.dart index e443ad1..7c06062 100644 --- a/lib/screens/metering/components/camera_container/components/camera_view/widget_camera_view.dart +++ b/lib/screens/metering/components/camera_container/components/camera_preview/components/camera_view/widget_camera_view.dart @@ -14,10 +14,12 @@ class CameraView extends StatelessWidget { valueListenable: controller, builder: (_, __, ___) => AspectRatio( aspectRatio: _isLandscape(value) ? value.aspectRatio : (1 / value.aspectRatio), - child: RotatedBox( - quarterTurns: _getQuarterTurns(value), - child: controller.buildPreview(), - ), + child: value.isInitialized + ? RotatedBox( + quarterTurns: _getQuarterTurns(value), + child: controller.buildPreview(), + ) + : const SizedBox.shrink(), ), ); } diff --git a/lib/screens/metering/components/camera_container/components/camera_view_placeholder/widget_placeholder_camera_view.dart b/lib/screens/metering/components/camera_container/components/camera_preview/components/camera_view_placeholder/widget_placeholder_camera_view.dart similarity index 100% rename from lib/screens/metering/components/camera_container/components/camera_view_placeholder/widget_placeholder_camera_view.dart rename to lib/screens/metering/components/camera_container/components/camera_preview/components/camera_view_placeholder/widget_placeholder_camera_view.dart diff --git a/lib/screens/metering/components/camera_container/components/camera_preview/components/histogram/widget_histogram.dart b/lib/screens/metering/components/camera_container/components/camera_preview/components/histogram/widget_histogram.dart new file mode 100644 index 0000000..6964e77 --- /dev/null +++ b/lib/screens/metering/components/camera_container/components/camera_preview/components/histogram/widget_histogram.dart @@ -0,0 +1,132 @@ +import 'dart:math'; + +import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; +import 'package:lightmeter/res/dimens.dart'; + +class CameraHistogram extends StatefulWidget { + final CameraController controller; + + const CameraHistogram({required this.controller, super.key}); + + @override + _CameraHistogramState createState() => _CameraHistogramState(); +} + +class _CameraHistogramState extends State { + List histogramR = List.filled(256, 0); + List histogramG = List.filled(256, 0); + List histogramB = List.filled(256, 0); + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + _startImageStream(); + }); + } + + @override + void dispose() { + widget.controller.stopImageStream(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + HistogramChannel( + color: Colors.red, + values: histogramR, + ), + const SizedBox(height: Dimens.grid4), + HistogramChannel( + color: Colors.green, + values: histogramG, + ), + const SizedBox(height: Dimens.grid4), + HistogramChannel( + color: Colors.blue, + values: histogramB, + ), + ], + ); + } + + void _startImageStream() { + widget.controller.startImageStream((CameraImage image) { + histogramR = List.filled(256, 0); + histogramG = List.filled(256, 0); + histogramB = List.filled(256, 0); + + final int uvRowStride = image.planes[1].bytesPerRow; + final int uvPixelStride = image.planes[1].bytesPerPixel!; + + for (int x = 0; x < image.width; x++) { + for (int y = 0; y < image.height; y++) { + final int uvIndex = uvPixelStride * (x / 2).floor() + uvRowStride * (y / 2).floor(); + final int index = y * image.width + x; + + final yp = image.planes[0].bytes[index]; + final up = image.planes[1].bytes[uvIndex]; + final vp = image.planes[2].bytes[uvIndex]; + + final r = yp + vp * 1436 / 1024 - 179; + final g = yp - up * 46549 / 131072 + 44 - vp * 93604 / 131072 + 91; + final b = yp + up * 1814 / 1024 - 227; + + histogramR[r.round().clamp(0, 255)]++; + histogramG[g.round().clamp(0, 255)]++; + histogramB[b.round().clamp(0, 255)]++; + } + } + + if (mounted) setState(() {}); + }); + } +} + +class HistogramChannel extends StatelessWidget { + final List values; + final Color color; + + final int _maxOccurences; + + HistogramChannel({ + required this.values, + required this.color, + super.key, + }) : _maxOccurences = values.reduce((value, element) => max(value, element)); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + final pixelWidth = constraints.maxWidth / values.length; + return Column( + children: [ + SizedBox( + height: Dimens.grid16, + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: values + .map( + (e) => SizedBox( + height: _maxOccurences == 0 ? 0 : Dimens.grid16 * (e / _maxOccurences), + width: pixelWidth, + child: ColoredBox(color: color), + ), + ) + .toList(), + ), + ), + const Divider(), + ], + ); + }, + ); + } +} 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 new file mode 100644 index 0000000..288c678 --- /dev/null +++ b/lib/screens/metering/components/camera_container/components/camera_preview/widget_camera_preview.dart @@ -0,0 +1,62 @@ +import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; +import 'package:lightmeter/data/models/metering_screen_layout_config.dart'; +import 'package:lightmeter/platform_config.dart'; +import 'package:lightmeter/providers/metering_screen_layout_provider.dart'; +import 'package:lightmeter/res/dimens.dart'; +import 'package:lightmeter/screens/metering/components/camera_container/components/camera_preview/components/camera_view/widget_camera_view.dart'; +import 'package:lightmeter/screens/metering/components/camera_container/components/camera_preview/components/camera_view_placeholder/widget_placeholder_camera_view.dart'; +import 'package:lightmeter/screens/metering/components/camera_container/components/camera_preview/components/histogram/widget_histogram.dart'; +import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart'; + +class CameraPreview extends StatefulWidget { + final CameraController? controller; + final CameraErrorType? error; + + const CameraPreview({this.controller, this.error, super.key}); + + @override + State createState() => _CameraPreviewState(); +} + +class _CameraPreviewState extends State { + @override + Widget build(BuildContext context) { + return AspectRatio( + aspectRatio: PlatformConfig.cameraPreviewAspectRatio, + child: Center( + child: Stack( + children: [ + const CameraViewPlaceholder(error: null), + 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 (MeteringScreenLayout.featureOf( + context, + MeteringScreenLayoutFeature.histogram, + )) + Positioned( + left: Dimens.grid8, + right: Dimens.grid8, + bottom: Dimens.grid16, + child: CameraHistogram(controller: widget.controller!), + ), + ], + ) + : const SizedBox.shrink(), + ) + : CameraViewPlaceholder(error: widget.error), + ), + ], + ), + ), + ); + } +} 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 7ac43eb..a8f0a11 100644 --- a/lib/screens/metering/components/camera_container/widget_container_camera.dart +++ b/lib/screens/metering/components/camera_container/widget_container_camera.dart @@ -10,8 +10,7 @@ import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/screens/metering/components/camera_container/bloc_container_camera.dart'; import 'package:lightmeter/screens/metering/components/camera_container/components/camera_controls/widget_camera_controls.dart'; import 'package:lightmeter/screens/metering/components/camera_container/components/camera_controls_placeholder/widget_placeholder_camera_controls.dart'; -import 'package:lightmeter/screens/metering/components/camera_container/components/camera_view/widget_camera_view.dart'; -import 'package:lightmeter/screens/metering/components/camera_container/components/camera_view_placeholder/widget_placeholder_camera_view.dart'; +import 'package:lightmeter/screens/metering/components/camera_container/components/camera_preview/widget_camera_preview.dart'; import 'package:lightmeter/screens/metering/components/camera_container/event_container_camera.dart'; import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart'; import 'package:lightmeter/screens/metering/components/camera_container/state_container_camera.dart'; @@ -107,20 +106,11 @@ class _CameraViewBuilder extends StatelessWidget { @override Widget build(BuildContext context) { - return AspectRatio( - aspectRatio: PlatformConfig.cameraPreviewAspectRatio, - child: BlocBuilder( - buildWhen: (previous, current) => current is! CameraActiveState, - builder: (context, state) => Center( - child: AnimatedSwitcher( - duration: Dimens.durationM, - child: switch (state) { - CameraInitializedState() => CameraView(controller: state.controller), - CameraErrorState() => CameraViewPlaceholder(error: state.error), - _ => const CameraViewPlaceholder(error: null), - }, - ), - ), + return BlocBuilder( + buildWhen: (previous, current) => current is! CameraActiveState, + builder: (context, state) => CameraPreview( + controller: state is CameraInitializedState ? state.controller : null, + error: state is CameraErrorState ? state.error : null, ), ); } @@ -161,7 +151,9 @@ class _CameraControlsBuilder extends StatelessWidget { }, ); } else { - child = const Column(children: [Expanded(child: SizedBox.shrink())],); + child = const Column( + children: [Expanded(child: SizedBox.shrink())], + ); } return AnimatedSwitcher( diff --git a/lib/screens/settings/components/metering/components/metering_screen_layout/components/meterins_screen_layout_features_dialog/widget_dialog_metering_screen_layout_features.dart b/lib/screens/settings/components/metering/components/metering_screen_layout/components/meterins_screen_layout_features_dialog/widget_dialog_metering_screen_layout_features.dart index 654a6c4..d43f011 100644 --- a/lib/screens/settings/components/metering/components/metering_screen_layout/components/meterins_screen_layout_features_dialog/widget_dialog_metering_screen_layout_features.dart +++ b/lib/screens/settings/components/metering/components/metering_screen_layout/components/meterins_screen_layout_features_dialog/widget_dialog_metering_screen_layout_features.dart @@ -71,6 +71,8 @@ class _MeteringScreenLayoutFeaturesDialogState extends State sharedPreferences.setString( UserPreferencesService.meteringScreenLayoutKey, - """{"0":false,"1":true}""", + """{"0":false,"1":true,"2":true}""", ), ).thenAnswer((_) => Future.value(true)); service.meteringScreenLayout = { MeteringScreenLayoutFeature.extremeExposurePairs: false, MeteringScreenLayoutFeature.filmPicker: true, + MeteringScreenLayoutFeature.histogram: true, }; verify( () => sharedPreferences.setString( UserPreferencesService.meteringScreenLayoutKey, - """{"0":false,"1":true}""", + """{"0":false,"1":true,"2":true}""", ), ).called(1); }); From 737a9aa2c252202264118024475cbf82f16a663b Mon Sep 17 00:00:00 2001 From: Vadim <44135514+vodemn@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:56:29 +0200 Subject: [PATCH 3/4] ML-98 Metering top bar cutout doesn't pass through taps (#99) * replaced `OverflowBox` with `Stack` --- .../widget_container_camera.dart | 155 +++++++++--------- 1 file changed, 76 insertions(+), 79 deletions(-) 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 a8f0a11..e42991c 100644 --- a/lib/screens/metering/components/camera_container/widget_container_camera.dart +++ b/lib/screens/metering/components/camera_container/widget_container_camera.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; @@ -45,59 +47,94 @@ class CameraContainer extends StatelessWidget { @override Widget build(BuildContext context) { - final double cameraViewHeight = - ((MediaQuery.of(context).size.width - Dimens.grid8 - 2 * Dimens.paddingM) / 2) / - PlatformConfig.cameraPreviewAspectRatio; + final double meteringContainerHeight = _meteringContainerHeight(context); + final double cameraPreviewHeight = _cameraPreviewHeight(context); + final double topBarOverflow = meteringContainerHeight - cameraPreviewHeight; - double topBarOverflow = Dimens.readingContainerSingleValueHeight + // ISO & ND - -cameraViewHeight; + return Stack( + children: [ + Positioned( + left: 0, + top: 0, + right: 0, + child: MeteringTopBar( + readingsContainer: ReadingsContainer( + fastest: fastest, + slowest: slowest, + film: film, + iso: iso, + nd: nd, + onFilmChanged: onFilmChanged, + onIsoChanged: onIsoChanged, + onNdChanged: onNdChanged, + ), + appendixHeight: topBarOverflow, + preview: const _CameraViewBuilder(), + ), + ), + SafeArea( + bottom: false, + child: Column( + children: [ + SizedBox( + height: min(meteringContainerHeight, cameraPreviewHeight) + Dimens.paddingM * 2, + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM), + child: Row( + children: [ + Expanded( + child: Padding( + padding: EdgeInsets.only(top: topBarOverflow >= 0 ? topBarOverflow : 0), + child: ExposurePairsList(exposurePairs), + ), + ), + const SizedBox(width: Dimens.grid8), + Expanded( + child: Padding( + padding: EdgeInsets.only(top: topBarOverflow <= 0 ? -topBarOverflow : 0), + child: const _CameraControlsBuilder(), + ), + ), + ], + ), + ), + ), + ], + ), + ) + ], + ); + } + + double _meteringContainerHeight(BuildContext context) { + double enabledFeaturesHeight = 0; if (FeaturesConfig.equipmentProfilesEnabled) { - topBarOverflow += Dimens.readingContainerSingleValueHeight; - topBarOverflow += Dimens.paddingS; + enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight; + enabledFeaturesHeight += Dimens.paddingS; } if (MeteringScreenLayout.featureOf( context, MeteringScreenLayoutFeature.extremeExposurePairs, )) { - topBarOverflow += Dimens.readingContainerDoubleValueHeight; - topBarOverflow += Dimens.paddingS; + enabledFeaturesHeight += Dimens.readingContainerDoubleValueHeight; + enabledFeaturesHeight += Dimens.paddingS; } if (MeteringScreenLayout.featureOf( context, MeteringScreenLayoutFeature.filmPicker, )) { - topBarOverflow += Dimens.readingContainerSingleValueHeight; - topBarOverflow += Dimens.paddingS; + enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight; + enabledFeaturesHeight += Dimens.paddingS; } - return Column( - children: [ - MeteringTopBar( - readingsContainer: ReadingsContainer( - fastest: fastest, - slowest: slowest, - film: film, - iso: iso, - nd: nd, - onFilmChanged: onFilmChanged, - onIsoChanged: onIsoChanged, - onNdChanged: onNdChanged, - ), - appendixHeight: topBarOverflow, - preview: const _CameraViewBuilder(), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM), - child: _MiddleContentWrapper( - topBarOverflow: topBarOverflow, - leftContent: ExposurePairsList(exposurePairs), - rightContent: const _CameraControlsBuilder(), - ), - ), - ), - ], - ); + return enabledFeaturesHeight + Dimens.readingContainerSingleValueHeight; // ISO & ND + } + + double _cameraPreviewHeight(BuildContext context) { + return ((MediaQuery.of(context).size.width - Dimens.grid8 - 2 * Dimens.paddingM) / 2) / + PlatformConfig.cameraPreviewAspectRatio; } } @@ -165,43 +202,3 @@ class _CameraControlsBuilder extends StatelessWidget { ); } } - -class _MiddleContentWrapper extends StatelessWidget { - final double topBarOverflow; - final Widget leftContent; - final Widget rightContent; - - const _MiddleContentWrapper({ - required this.topBarOverflow, - required this.leftContent, - required this.rightContent, - }); - - @override - Widget build(BuildContext context) { - return LayoutBuilder( - builder: (context, constraints) => OverflowBox( - alignment: Alignment.bottomCenter, - maxHeight: constraints.maxHeight + topBarOverflow.abs(), - maxWidth: constraints.maxWidth, - child: Row( - children: [ - Expanded( - child: Padding( - padding: EdgeInsets.only(top: topBarOverflow >= 0 ? topBarOverflow : 0), - child: leftContent, - ), - ), - const SizedBox(width: Dimens.grid8), - Expanded( - child: Padding( - padding: EdgeInsets.only(top: topBarOverflow <= 0 ? -topBarOverflow : 0), - child: rightContent, - ), - ), - ], - ), - ), - ); - } -} From 9c11401175c344d5d69f5adfaf0e7eea4b25b558 Mon Sep 17 00:00:00 2001 From: vodemn Date: Mon, 7 Aug 2023 14:59:47 +0000 Subject: [PATCH 4/4] Version bump --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 315e72f..7a4c862 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: lightmeter description: A new Flutter project. publish_to: "none" -version: 0.12.4+35 +version: 0.13.0+36 environment: sdk: ">=3.0.0 <4.0.0"