mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2025-07-07 14:40:40 +00:00
Compare commits
No commits in common. "bf3c8aa7c7396238ae01a43ed6619c83b506e093" and "d364de44864a836f422544565e334ddc6fb55ce5" have entirely different histories.
bf3c8aa7c7
...
d364de4486
50 changed files with 298 additions and 1623 deletions
8
.github/scripts/increment_build_number.sh
vendored
8
.github/scripts/increment_build_number.sh
vendored
|
@ -1,8 +0,0 @@
|
||||||
export newVersion="$1"
|
|
||||||
|
|
||||||
if [[ -n "$newVersion" ]]; then
|
|
||||||
#https://stackoverflow.com/a/30214769/13167574
|
|
||||||
perl -i -pe 's/^(version:\s+)(\d+\.\d+\.\d+)(\+)(\d+)$/$1.$ENV{'newVersion'}.$3.($4+1)/e' pubspec.yaml
|
|
||||||
else
|
|
||||||
echo "argument error"
|
|
||||||
fi
|
|
2
.github/scripts/stub_iap.sh
vendored
2
.github/scripts/stub_iap.sh
vendored
|
@ -1,2 +0,0 @@
|
||||||
# https://unix.stackexchange.com/questions/435708/regex-multiline-pattern-and-substitution-replacement
|
|
||||||
perl -0777 -i -pe 's/( m3_lightmeter_iap:\n)( git:\n url: "https:\/\/github.com\/vodemn\/m3_lightmeter_iap"\n ref: main)/$1 path: iap/sg' pubspec.yaml
|
|
19
.github/workflows/build_apk.yml
vendored
19
.github/workflows/build_apk.yml
vendored
|
@ -16,31 +16,18 @@ on:
|
||||||
- dev
|
- dev
|
||||||
- prod
|
- prod
|
||||||
default: 'dev'
|
default: 'dev'
|
||||||
include-iap:
|
|
||||||
type: boolean
|
|
||||||
description: Include IAP package
|
|
||||||
default: true
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build .apk
|
name: Build .apk
|
||||||
runs-on: macos-11
|
runs-on: macos-11
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Connect private iap package
|
|
||||||
uses: webfactory/ssh-agent@v0.8.0
|
|
||||||
if: ${{ inputs.include-iap }}
|
|
||||||
with:
|
|
||||||
ssh-private-key: ${{ secrets.M3_LIGHTMETER_IAP_KEY }}
|
|
||||||
|
|
||||||
- name: Override iap package with stub
|
|
||||||
if: ${{ !inputs.include-iap }}
|
|
||||||
run: bash ./.github/scripts/stub_iap.sh
|
|
||||||
|
|
||||||
- uses: actions/setup-java@v2
|
- uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
distribution: "zulu"
|
distribution: "zulu"
|
||||||
|
@ -86,10 +73,10 @@ jobs:
|
||||||
flutter pub get
|
flutter pub get
|
||||||
flutter pub run intl_utils:generate
|
flutter pub run intl_utils:generate
|
||||||
|
|
||||||
- name: Build .apk
|
- name: Build Apk
|
||||||
env:
|
env:
|
||||||
FLAVOR: ${{ github.event.inputs.flavor }}
|
FLAVOR: ${{ github.event.inputs.flavor }}
|
||||||
run: flutter build apk --release --flavor $FLAVOR --dart-define cameraPreviewAspectRatio=240/320 -t lib/main_$FLAVOR.dart
|
run: flutter build apk --release --flavor $FLAVOR --dart-define c -t lib/main_$FLAVOR.dart
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
|
20
.github/workflows/create_release.yml
vendored
20
.github/workflows/create_release.yml
vendored
|
@ -31,10 +31,6 @@ on:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Create Google Play release
|
description: Create Google Play release
|
||||||
default: true
|
default: true
|
||||||
include-iap:
|
|
||||||
type: boolean
|
|
||||||
description: Include IAP package
|
|
||||||
default: true
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILD_ARGS: --release --flavor prod --dart-define cameraPreviewAspectRatio=240/320 -t lib/main_prod.dart
|
BUILD_ARGS: --release --flavor prod --dart-define cameraPreviewAspectRatio=240/320 -t lib/main_prod.dart
|
||||||
|
@ -42,7 +38,7 @@ env:
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build .apk & .aab
|
name: Build .apk & .aab
|
||||||
if: ${{ inputs.github-release || inputs.google-play-release }}
|
if: ${{ inputs.github-release }} || ${{ inputs.google-play-release }}
|
||||||
runs-on: macos-11
|
runs-on: macos-11
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
steps:
|
steps:
|
||||||
|
@ -50,16 +46,6 @@ jobs:
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Connect private iap package
|
|
||||||
uses: webfactory/ssh-agent@v0.8.0
|
|
||||||
if: ${{ inputs.include-iap }}
|
|
||||||
with:
|
|
||||||
ssh-private-key: ${{ secrets.M3_LIGHTMETER_IAP_KEY }}
|
|
||||||
|
|
||||||
- name: Override iap package with stub
|
|
||||||
if: ${{ !inputs.include-iap }}
|
|
||||||
run: bash ./.github/scripts/stub_iap.sh
|
|
||||||
|
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: "zulu"
|
distribution: "zulu"
|
||||||
|
@ -97,7 +83,7 @@ jobs:
|
||||||
# Therefore here we have to increment it as well to build an apk with the same build number.
|
# Therefore here we have to increment it as well to build an apk with the same build number.
|
||||||
- name: Increment build number & replace version number
|
- name: Increment build number & replace version number
|
||||||
if: ${{ inputs.github-release }}
|
if: ${{ inputs.github-release }}
|
||||||
run: bash ./.github/scripts/increment_build_number.sh ${{ github.event.inputs.version }}
|
run: perl -i -pe 's/^(version:\s+)(\d+\.\d+\.\d+)(\+)(\d+)$/$1."${{ github.event.inputs.version }}".$3.($4+1)/e' pubspec.yaml
|
||||||
|
|
||||||
- name: Install Flutter
|
- name: Install Flutter
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
|
@ -159,7 +145,7 @@ jobs:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Increment build number & replace version number
|
- name: Increment build number & replace version number
|
||||||
run: bash ./.github/scripts/increment_build_number.sh ${{ github.event.inputs.version }}
|
run: perl -i -pe 's/^(version:\s+)(\d+\.\d+\.\d+)(\+)(\d+)$/$1."${{ github.event.inputs.version }}".$3.($4+1)/e' pubspec.yaml
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
run: |
|
run: |
|
||||||
|
|
15
.github/workflows/pr_check.yml
vendored
15
.github/workflows/pr_check.yml
vendored
|
@ -16,25 +16,16 @@ jobs:
|
||||||
name: Analyze & test
|
name: Analyze & test
|
||||||
runs-on: macos-11
|
runs-on: macos-11
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Connect private iap package
|
|
||||||
uses: webfactory/ssh-agent@v0.8.0
|
|
||||||
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
|
|
||||||
with:
|
|
||||||
ssh-private-key: ${{ secrets.M3_LIGHTMETER_IAP_KEY }}
|
|
||||||
|
|
||||||
- name: Override iap package with stub
|
|
||||||
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
|
|
||||||
run: bash ./.github/scripts/stub_iap.sh
|
|
||||||
|
|
||||||
- uses: subosito/flutter-action@v2
|
- uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
channel: "stable"
|
channel: "stable"
|
||||||
flutter-version: "3.10.0"
|
flutter-version: '3.10.0'
|
||||||
|
|
||||||
- name: Prepare flutter project
|
- name: Prepare flutter project
|
||||||
run: |
|
run: |
|
||||||
|
@ -46,4 +37,4 @@ jobs:
|
||||||
run: flutter analyze lib --fatal-infos
|
run: flutter analyze lib --fatal-infos
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: flutter test
|
run: flutter test
|
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
|
@ -31,7 +31,6 @@
|
||||||
{
|
{
|
||||||
"name": "prod (android)",
|
"name": "prod (android)",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
//"flutterMode": "release",
|
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"args": [
|
"args": [
|
||||||
"--flavor",
|
"--flavor",
|
||||||
|
@ -44,7 +43,6 @@
|
||||||
{
|
{
|
||||||
"name": "prod (ios)",
|
"name": "prod (ios)",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
//"flutterMode": "release",
|
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"args": [
|
"args": [
|
||||||
"--flavor",
|
"--flavor",
|
||||||
|
|
19
README.md
19
README.md
|
@ -39,22 +39,7 @@ Out of the box Firebase Crashlytics won't work. If you want to add Crashlytics t
|
||||||
|
|
||||||
### 3. Get packages
|
### 3. Get packages
|
||||||
|
|
||||||
As part of the app's functionallity is in the private repo, you have to replace these lines in _pubspec.yaml_:
|
Fetch all the neccessary dependencies and generate translation files by running the following commands:
|
||||||
|
|
||||||
```yaml
|
|
||||||
m3_lightmeter_iap:
|
|
||||||
git:
|
|
||||||
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.
|
|
||||||
|
|
||||||
Then you can fetch all the neccessary dependencies and generate translation files by running the following commands:
|
|
||||||
```console
|
```console
|
||||||
flutter pub get
|
flutter pub get
|
||||||
flutter pub run intl_utils:generate
|
flutter pub run intl_utils:generate
|
||||||
|
@ -84,4 +69,4 @@ Apple does not provide API for reading Lux stream form the ambient light sensor.
|
||||||
|
|
||||||
## Volume buttons action
|
## Volume buttons action
|
||||||
|
|
||||||
This can be [implemented](https://stackoverflow.com/questions/70161271/ios-override-hardware-volume-buttons-same-as-zello) but the app will be rejected due to [2.5.9](https://developer.apple.com/app-store/review/guidelines/#software-requirements)
|
This can be [implemented](https://stackoverflow.com/questions/70161271/ios-override-hardware-volume-buttons-same-as-zello) but the app will be rejected due to [2.5.9](https://developer.apple.com/app-store/review/guidelines/#software-requirements)
|
36
iap/.gitignore
vendored
36
iap/.gitignore
vendored
|
@ -1,36 +0,0 @@
|
||||||
# Miscellaneous
|
|
||||||
*.class
|
|
||||||
*.log
|
|
||||||
*.pyc
|
|
||||||
*.swp
|
|
||||||
.DS_Store
|
|
||||||
.atom/
|
|
||||||
.buildlog/
|
|
||||||
.history
|
|
||||||
.svn/
|
|
||||||
migrate_working_dir/
|
|
||||||
|
|
||||||
# IntelliJ related
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
*.iws
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# The .vscode folder contains launch configuration and tasks you configure in
|
|
||||||
# VS Code which you may wish to be included in version control, so this line
|
|
||||||
# is commented out by default.
|
|
||||||
#.vscode/
|
|
||||||
|
|
||||||
# Flutter/Dart/Pub related
|
|
||||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
|
||||||
/pubspec.lock
|
|
||||||
**/doc/api/
|
|
||||||
.dart_tool/
|
|
||||||
.packages
|
|
||||||
build/
|
|
||||||
|
|
||||||
.fvm/
|
|
||||||
*.properties
|
|
||||||
ios/Flutter/
|
|
||||||
.flutter-plugins
|
|
||||||
.flutter-plugins-dependencies
|
|
|
@ -1,10 +0,0 @@
|
||||||
# This file tracks properties of this Flutter project.
|
|
||||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
|
||||||
#
|
|
||||||
# This file should be version controlled and should not be manually edited.
|
|
||||||
|
|
||||||
version:
|
|
||||||
revision: 9944297138845a94256f1cf37beb88ff9a8e811a
|
|
||||||
channel: stable
|
|
||||||
|
|
||||||
project_type: package
|
|
|
@ -1 +0,0 @@
|
||||||
TODO: Add your license here.
|
|
|
@ -1,4 +0,0 @@
|
||||||
include: package:flutter_lints/flutter.yaml
|
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
|
@ -1,30 +0,0 @@
|
||||||
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/iap_products_provider.dart';
|
|
||||||
|
|
||||||
export 'src/data/models/iap_product.dart';
|
|
||||||
|
|
||||||
export 'src/providers/equipment_profile_provider.dart' hide EquipmentProfilesAspect;
|
|
||||||
export 'src/providers/iap_products_provider.dart';
|
|
||||||
|
|
||||||
class IAPProviders extends StatelessWidget {
|
|
||||||
final Object sharedPreferences;
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
const IAPProviders({
|
|
||||||
required this.sharedPreferences,
|
|
||||||
required this.child,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return IAPProductsProvider(
|
|
||||||
child: EquipmentProfileProvider(
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
enum IAPProductStatus {
|
|
||||||
purchasable,
|
|
||||||
pending,
|
|
||||||
purchased,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum IAPProductType { paidFeatures }
|
|
||||||
|
|
||||||
abstract class IAPProduct {
|
|
||||||
const IAPProduct._();
|
|
||||||
|
|
||||||
IAPProductStatus get status => IAPProductStatus.purchasable;
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
|
||||||
|
|
||||||
class EquipmentProfileProvider extends StatefulWidget {
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
const EquipmentProfileProvider({required this.child, super.key});
|
|
||||||
|
|
||||||
static EquipmentProfileProviderState of(BuildContext context) {
|
|
||||||
return context.findAncestorStateOfType<EquipmentProfileProviderState>()!;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<EquipmentProfileProvider> createState() => EquipmentProfileProviderState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class EquipmentProfileProviderState extends State<EquipmentProfileProvider> {
|
|
||||||
static const EquipmentProfile _defaultProfile = EquipmentProfile(
|
|
||||||
id: '',
|
|
||||||
name: '',
|
|
||||||
apertureValues: ApertureValue.values,
|
|
||||||
ndValues: NdValue.values,
|
|
||||||
shutterSpeedValues: ShutterSpeedValue.values,
|
|
||||||
isoValues: IsoValue.values,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return EquipmentProfiles(
|
|
||||||
profiles: const [_defaultProfile],
|
|
||||||
selected: _defaultProfile,
|
|
||||||
child: widget.child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setProfile(EquipmentProfile data) {}
|
|
||||||
|
|
||||||
void addProfile(String name) {}
|
|
||||||
|
|
||||||
void updateProdile(EquipmentProfile data) {}
|
|
||||||
|
|
||||||
void deleteProfile(EquipmentProfile data) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum EquipmentProfilesAspect { list, selected }
|
|
||||||
|
|
||||||
class EquipmentProfiles extends InheritedModel<EquipmentProfilesAspect> {
|
|
||||||
const EquipmentProfiles({
|
|
||||||
super.key,
|
|
||||||
required this.profiles,
|
|
||||||
required this.selected,
|
|
||||||
required super.child,
|
|
||||||
});
|
|
||||||
|
|
||||||
final List<EquipmentProfile> profiles;
|
|
||||||
final EquipmentProfile selected;
|
|
||||||
|
|
||||||
static List<EquipmentProfile> of(BuildContext context) {
|
|
||||||
return InheritedModel.inheritFrom<EquipmentProfiles>(
|
|
||||||
context,
|
|
||||||
aspect: EquipmentProfilesAspect.list,
|
|
||||||
)!
|
|
||||||
.profiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
static EquipmentProfile selectedOf(BuildContext context) {
|
|
||||||
return InheritedModel.inheritFrom<EquipmentProfiles>(
|
|
||||||
context,
|
|
||||||
aspect: EquipmentProfilesAspect.selected,
|
|
||||||
)!
|
|
||||||
.selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool updateShouldNotify(EquipmentProfiles oldWidget) => false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool updateShouldNotifyDependent(EquipmentProfiles oldWidget, Set<EquipmentProfilesAspect> dependencies) => false;
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:m3_lightmeter_iap/src/data/models/iap_product.dart';
|
|
||||||
|
|
||||||
class IAPProductsProvider extends StatefulWidget {
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
const IAPProductsProvider({required this.child, super.key});
|
|
||||||
|
|
||||||
static IAPProductsProviderState of(BuildContext context) {
|
|
||||||
return context.findAncestorStateOfType<IAPProductsProviderState>()!;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<IAPProductsProvider> createState() => IAPProductsProviderState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class IAPProductsProviderState extends State<IAPProductsProvider> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return IAPProducts(
|
|
||||||
products: const [],
|
|
||||||
child: widget.child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> buy(IAPProductType type) async {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IAPProducts extends InheritedModel<IAPProductType> {
|
|
||||||
final List<IAPProduct> products;
|
|
||||||
|
|
||||||
const IAPProducts({
|
|
||||||
required this.products,
|
|
||||||
required super.child,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
static IAPProduct? productOf(BuildContext context, IAPProductType type) => null;
|
|
||||||
|
|
||||||
static bool isPurchased(BuildContext context, IAPProductType type) => false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool updateShouldNotify(IAPProducts oldWidget) => false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool updateShouldNotifyDependent(covariant IAPProducts oldWidget, Set<IAPProductType> dependencies) => false;
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
name: m3_lightmeter_iap
|
|
||||||
description: IAP stubs for the M3 Lightmeter app.
|
|
||||||
version: 0.2.0
|
|
||||||
publish_to: 'none'
|
|
||||||
|
|
||||||
environment:
|
|
||||||
sdk: '>=2.19.2 <3.0.0'
|
|
||||||
flutter: ">=1.17.0"
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
m3_lightmeter_resources:
|
|
||||||
git:
|
|
||||||
url: "https://github.com/vodemn/m3_lightmeter_resources"
|
|
||||||
ref: main
|
|
||||||
shared_preferences: 2.2.0
|
|
||||||
|
|
||||||
dev_dependencies:
|
|
||||||
flutter_test:
|
|
||||||
sdk: flutter
|
|
||||||
flutter_lints: ^2.0.0
|
|
||||||
|
|
||||||
flutter:
|
|
||||||
uses-material-design: true
|
|
|
@ -3,7 +3,7 @@
|
||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 54;
|
objectVersion = 51;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
@ -237,7 +237,6 @@
|
||||||
};
|
};
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
|
@ -269,7 +268,6 @@
|
||||||
};
|
};
|
||||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
|
@ -373,7 +371,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = 489Z6UQMGN;
|
DEVELOPMENT_TEAM = 74JQ9DBXY6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -502,7 +500,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = 489Z6UQMGN;
|
DEVELOPMENT_TEAM = 74JQ9DBXY6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -525,7 +523,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = 489Z6UQMGN;
|
DEVELOPMENT_TEAM = 74JQ9DBXY6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -602,7 +600,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = 489Z6UQMGN;
|
DEVELOPMENT_TEAM = 74JQ9DBXY6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -677,7 +675,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = 489Z6UQMGN;
|
DEVELOPMENT_TEAM = 74JQ9DBXY6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
@ -749,7 +747,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = 489Z6UQMGN;
|
DEVELOPMENT_TEAM = 74JQ9DBXY6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
|
|
@ -10,11 +10,11 @@ import 'package:lightmeter/data/shared_prefs_service.dart';
|
||||||
import 'package:lightmeter/data/volume_events_service.dart';
|
import 'package:lightmeter/data/volume_events_service.dart';
|
||||||
import 'package:lightmeter/environment.dart';
|
import 'package:lightmeter/environment.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
|
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
||||||
import 'package:lightmeter/providers/services_provider.dart';
|
import 'package:lightmeter/providers/services_provider.dart';
|
||||||
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
||||||
import 'package:lightmeter/screens/metering/flow_metering.dart';
|
import 'package:lightmeter/screens/metering/flow_metering.dart';
|
||||||
import 'package:lightmeter/screens/settings/flow_settings.dart';
|
import 'package:lightmeter/screens/settings/flow_settings.dart';
|
||||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
|
||||||
import 'package:platform/platform.dart';
|
import 'package:platform/platform.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
@ -32,18 +32,16 @@ class Application extends StatelessWidget {
|
||||||
]),
|
]),
|
||||||
builder: (_, snapshot) {
|
builder: (_, snapshot) {
|
||||||
if (snapshot.data != null) {
|
if (snapshot.data != null) {
|
||||||
return IAPProviders(
|
return ServicesProvider(
|
||||||
sharedPreferences: snapshot.data![0] as SharedPreferences,
|
caffeineService: const CaffeineService(),
|
||||||
child: ServicesProvider(
|
environment: env.copyWith(hasLightSensor: snapshot.data![1] as bool),
|
||||||
caffeineService: const CaffeineService(),
|
hapticsService: const HapticsService(),
|
||||||
environment: env.copyWith(hasLightSensor: snapshot.data![1] as bool),
|
lightSensorService: const LightSensorService(LocalPlatform()),
|
||||||
hapticsService: const HapticsService(),
|
permissionsService: const PermissionsService(),
|
||||||
lightSensorService: const LightSensorService(LocalPlatform()),
|
userPreferencesService: UserPreferencesService(snapshot.data![0] as SharedPreferences),
|
||||||
permissionsService: const PermissionsService(),
|
volumeEventsService: const VolumeEventsService(LocalPlatform()),
|
||||||
userPreferencesService:
|
child: UserPreferencesProvider(
|
||||||
UserPreferencesService(snapshot.data![0] as SharedPreferences),
|
child: EquipmentProfileProvider(
|
||||||
volumeEventsService: const VolumeEventsService(LocalPlatform()),
|
|
||||||
child: UserPreferencesProvider(
|
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final theme = UserPreferencesProvider.themeOf(context);
|
final theme = UserPreferencesProvider.themeOf(context);
|
||||||
|
|
|
@ -8,16 +8,4 @@ class ExposurePair {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => '$aperture - $shutterSpeed';
|
String toString() => '$aperture - $shutterSpeed';
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if (identical(this, other)) return true;
|
|
||||||
if (other.runtimeType != runtimeType) return false;
|
|
||||||
return other is ExposurePair &&
|
|
||||||
other.aperture == aperture &&
|
|
||||||
other.shutterSpeed == shutterSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(aperture, shutterSpeed, runtimeType);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,6 @@ double log10polynomian(
|
||||||
) =>
|
) =>
|
||||||
a * pow(log10(x), 2) + b * log10(x) + 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:
|
/// Only Ilford films have reciprocity formulas provided by the manufacturer:
|
||||||
/// https://www.ilfordphoto.com/wp/wp-content/uploads/2017/06/Reciprocity-Failure-Compensation.pdf
|
/// https://www.ilfordphoto.com/wp/wp-content/uploads/2017/06/Reciprocity-Failure-Compensation.pdf
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
enum MeteringScreenLayoutFeature {
|
enum MeteringScreenLayoutFeature { extremeExposurePairs, filmPicker, histogram }
|
||||||
extremeExposurePairs,
|
|
||||||
filmPicker,
|
|
||||||
histogram,
|
|
||||||
equipmentProfiles,
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef MeteringScreenLayoutConfig = Map<MeteringScreenLayoutFeature, bool>;
|
typedef MeteringScreenLayoutConfig = Map<MeteringScreenLayoutFeature, bool>;
|
||||||
|
|
||||||
extension MeteringScreenLayoutConfigJson on MeteringScreenLayoutConfig {
|
extension MeteringScreenLayoutConfigJson on MeteringScreenLayoutConfig {
|
||||||
static MeteringScreenLayoutConfig fromJson(Map<String, dynamic> data) =>
|
static MeteringScreenLayoutConfig fromJson(Map<String, dynamic> data) =>
|
||||||
<MeteringScreenLayoutFeature, bool>{
|
<MeteringScreenLayoutFeature, bool>{
|
||||||
for (final f in MeteringScreenLayoutFeature.values)
|
for (final f in MeteringScreenLayoutFeature.values)
|
||||||
f: data[f.index.toString()] as bool? ?? true
|
f: data[f.index.toString()] as bool? ?? true
|
||||||
|
|
|
@ -95,7 +95,6 @@ class UserPreferencesService {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
MeteringScreenLayoutFeature.equipmentProfiles: true,
|
|
||||||
MeteringScreenLayoutFeature.extremeExposurePairs: true,
|
MeteringScreenLayoutFeature.extremeExposurePairs: true,
|
||||||
MeteringScreenLayoutFeature.filmPicker: true,
|
MeteringScreenLayoutFeature.filmPicker: true,
|
||||||
MeteringScreenLayoutFeature.histogram: true,
|
MeteringScreenLayoutFeature.histogram: true,
|
||||||
|
@ -148,4 +147,10 @@ class UserPreferencesService {
|
||||||
orElse: () => Film.values.first,
|
orElse: () => Film.values.first,
|
||||||
);
|
);
|
||||||
set film(Film value) => _sharedPreferences.setString(filmKey, value.name);
|
set film(Film value) => _sharedPreferences.setString(filmKey, value.name);
|
||||||
|
|
||||||
|
String get selectedEquipmentProfileId => ''; // coverage:ignore-line
|
||||||
|
set selectedEquipmentProfileId(String id) {} // coverage:ignore-line
|
||||||
|
|
||||||
|
List<EquipmentProfile> get equipmentProfiles => []; // coverage:ignore-line
|
||||||
|
set equipmentProfiles(List<EquipmentProfile> profiles) {} // coverage:ignore-line
|
||||||
}
|
}
|
||||||
|
|
3
lib/features.dart
Normal file
3
lib/features.dart
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
class FeaturesConfig {
|
||||||
|
static const bool equipmentProfilesEnabled = false;
|
||||||
|
}
|
|
@ -54,7 +54,6 @@
|
||||||
"isoValuesFilterDescription": "Select the ISO values to display. These may be your most commonly used values or those supported by your camera.",
|
"isoValuesFilterDescription": "Select the ISO values to display. These may be your most commonly used values or those supported by your camera.",
|
||||||
"equipmentProfile": "Equipment profile",
|
"equipmentProfile": "Equipment profile",
|
||||||
"equipmentProfiles": "Equipment profiles",
|
"equipmentProfiles": "Equipment profiles",
|
||||||
"tapToAdd": "Tap to add",
|
|
||||||
"general": "General",
|
"general": "General",
|
||||||
"keepScreenOn": "Keep screen on",
|
"keepScreenOn": "Keep screen on",
|
||||||
"haptics": "Haptics",
|
"haptics": "Haptics",
|
||||||
|
@ -87,4 +86,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -42,7 +42,6 @@
|
||||||
"film": "Pellicule",
|
"film": "Pellicule",
|
||||||
"equipment": "Équipement",
|
"equipment": "Équipement",
|
||||||
"equipmentProfileName": "Nom du profil de l'équipement",
|
"equipmentProfileName": "Nom du profil de l'équipement",
|
||||||
"tapToAdd": "Appuie pour ajouter",
|
|
||||||
"equipmentProfileNameHint": "Praktica MTL5B",
|
"equipmentProfileNameHint": "Praktica MTL5B",
|
||||||
"equipmentProfileAllValues": "Tout",
|
"equipmentProfileAllValues": "Tout",
|
||||||
"apertureValues": "Valeurs Aperture",
|
"apertureValues": "Valeurs Aperture",
|
||||||
|
@ -87,4 +86,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -54,7 +54,6 @@
|
||||||
"isoValuesFilterDescription": "Выберите значения ISO для отображения. Это может быть наиболее часто используемые значения или значения, поддерживаемые вашей камерой.",
|
"isoValuesFilterDescription": "Выберите значения ISO для отображения. Это может быть наиболее часто используемые значения или значения, поддерживаемые вашей камерой.",
|
||||||
"equipmentProfile": "Оборудование",
|
"equipmentProfile": "Оборудование",
|
||||||
"equipmentProfiles": "Профили оборудования",
|
"equipmentProfiles": "Профили оборудования",
|
||||||
"tapToAdd": "Нажмите, чтобы добавить",
|
|
||||||
"general": "Общие",
|
"general": "Общие",
|
||||||
"keepScreenOn": "Запрет блокировки",
|
"keepScreenOn": "Запрет блокировки",
|
||||||
"haptics": "Вибрация",
|
"haptics": "Вибрация",
|
||||||
|
@ -87,4 +86,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -54,7 +54,6 @@
|
||||||
"isoValuesFilterDescription": "选择要显示的 ISO。这些值可能是您最常用的值,也可能是相机支持的值。",
|
"isoValuesFilterDescription": "选择要显示的 ISO。这些值可能是您最常用的值,也可能是相机支持的值。",
|
||||||
"equipmentProfile": "设备配置",
|
"equipmentProfile": "设备配置",
|
||||||
"equipmentProfiles": "设备配置",
|
"equipmentProfiles": "设备配置",
|
||||||
"tapToAdd": "點擊添加",
|
|
||||||
"general": "通用",
|
"general": "通用",
|
||||||
"keepScreenOn": "保持屏幕常亮",
|
"keepScreenOn": "保持屏幕常亮",
|
||||||
"haptics": "震动",
|
"haptics": "震动",
|
||||||
|
|
139
lib/providers/equipment_profile_provider.dart
Normal file
139
lib/providers/equipment_profile_provider.dart
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/providers/services_provider.dart';
|
||||||
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
// TODO(@vodemn): This will be removed in #89
|
||||||
|
class EquipmentProfileProvider extends StatefulWidget {
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
const EquipmentProfileProvider({required this.child, super.key});
|
||||||
|
|
||||||
|
static EquipmentProfileProviderState of(BuildContext context) {
|
||||||
|
return context.findAncestorStateOfType<EquipmentProfileProviderState>()!;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<EquipmentProfileProvider> createState() => EquipmentProfileProviderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class EquipmentProfileProviderState extends State<EquipmentProfileProvider> {
|
||||||
|
static const EquipmentProfile _defaultProfile = EquipmentProfile(
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
apertureValues: ApertureValue.values,
|
||||||
|
ndValues: NdValue.values,
|
||||||
|
shutterSpeedValues: ShutterSpeedValue.values,
|
||||||
|
isoValues: IsoValue.values,
|
||||||
|
);
|
||||||
|
|
||||||
|
List<EquipmentProfile> _customProfiles = [];
|
||||||
|
String _selectedId = '';
|
||||||
|
|
||||||
|
EquipmentProfile get _selectedProfile => _customProfiles.firstWhere(
|
||||||
|
(e) => e.id == _selectedId,
|
||||||
|
orElse: () {
|
||||||
|
ServicesProvider.of(context).userPreferencesService.selectedEquipmentProfileId =
|
||||||
|
_defaultProfile.id;
|
||||||
|
return _defaultProfile;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_selectedId = ServicesProvider.of(context).userPreferencesService.selectedEquipmentProfileId;
|
||||||
|
_customProfiles = ServicesProvider.of(context).userPreferencesService.equipmentProfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return EquipmentProfiles(
|
||||||
|
profiles: [_defaultProfile] + _customProfiles,
|
||||||
|
selected: _selectedProfile,
|
||||||
|
child: widget.child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setProfile(EquipmentProfile data) {
|
||||||
|
setState(() {
|
||||||
|
_selectedId = data.id;
|
||||||
|
});
|
||||||
|
ServicesProvider.of(context).userPreferencesService.selectedEquipmentProfileId =
|
||||||
|
_selectedProfile.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a default equipment profile
|
||||||
|
void addProfile(String name) {
|
||||||
|
_customProfiles.add(
|
||||||
|
EquipmentProfile(
|
||||||
|
id: const Uuid().v1(),
|
||||||
|
name: name,
|
||||||
|
apertureValues: ApertureValue.values,
|
||||||
|
ndValues: NdValue.values,
|
||||||
|
shutterSpeedValues: ShutterSpeedValue.values,
|
||||||
|
isoValues: IsoValue.values,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_refreshSavedProfiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateProdile(EquipmentProfile data) {
|
||||||
|
final indexToUpdate = _customProfiles.indexWhere((element) => element.id == data.id);
|
||||||
|
if (indexToUpdate >= 0) {
|
||||||
|
_customProfiles[indexToUpdate] = data;
|
||||||
|
_refreshSavedProfiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteProfile(EquipmentProfile data) {
|
||||||
|
_customProfiles.remove(data);
|
||||||
|
_refreshSavedProfiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _refreshSavedProfiles() {
|
||||||
|
ServicesProvider.of(context).userPreferencesService.equipmentProfiles = _customProfiles;
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copied from #89
|
||||||
|
enum EquipmentProfilesAspect { list, selected }
|
||||||
|
|
||||||
|
class EquipmentProfiles extends InheritedModel<EquipmentProfilesAspect> {
|
||||||
|
const EquipmentProfiles({
|
||||||
|
super.key,
|
||||||
|
required this.profiles,
|
||||||
|
required this.selected,
|
||||||
|
required super.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<EquipmentProfile> profiles;
|
||||||
|
final EquipmentProfile selected;
|
||||||
|
|
||||||
|
static List<EquipmentProfile> of(BuildContext context) {
|
||||||
|
return InheritedModel.inheritFrom<EquipmentProfiles>(
|
||||||
|
context,
|
||||||
|
aspect: EquipmentProfilesAspect.list,
|
||||||
|
)!
|
||||||
|
.profiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EquipmentProfile selectedOf(BuildContext context) {
|
||||||
|
return InheritedModel.inheritFrom<EquipmentProfiles>(
|
||||||
|
context,
|
||||||
|
aspect: EquipmentProfilesAspect.selected,
|
||||||
|
)!
|
||||||
|
.selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateShouldNotify(EquipmentProfiles oldWidget) => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateShouldNotifyDependent(
|
||||||
|
EquipmentProfiles oldWidget,
|
||||||
|
Set<EquipmentProfilesAspect> dependencies,
|
||||||
|
) =>
|
||||||
|
false;
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ class Dimens {
|
||||||
static const double grid48 = 48;
|
static const double grid48 = 48;
|
||||||
static const double grid56 = 56;
|
static const double grid56 = 56;
|
||||||
static const double grid72 = 72;
|
static const double grid72 = 72;
|
||||||
|
static const double grid168 = 168;
|
||||||
|
|
||||||
static const double paddingS = 8;
|
static const double paddingS = 8;
|
||||||
static const double paddingM = 16;
|
static const double paddingM = 16;
|
||||||
|
@ -29,8 +30,6 @@ class Dimens {
|
||||||
static const double enabledOpacity = 1.0;
|
static const double enabledOpacity = 1.0;
|
||||||
static const double disabledOpacity = 0.38;
|
static const double disabledOpacity = 0.38;
|
||||||
|
|
||||||
static const double sliverAppBarExpandedHeight = 168;
|
|
||||||
|
|
||||||
// TopBar
|
// TopBar
|
||||||
static const double readingContainerDoubleValueHeight = 128;
|
static const double readingContainerDoubleValueHeight = 128;
|
||||||
static const double readingContainerSingleValueHeight = 76;
|
static const double readingContainerSingleValueHeight = 76;
|
||||||
|
|
|
@ -18,7 +18,7 @@ import 'package:lightmeter/screens/metering/components/camera_container/event_co
|
||||||
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.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';
|
import 'package:lightmeter/screens/metering/components/camera_container/state_container_camera.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/ev_source_base/bloc_base_ev_source.dart';
|
import 'package:lightmeter/screens/metering/components/shared/ev_source_base/bloc_base_ev_source.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:lightmeter/utils/log_2.dart';
|
||||||
|
|
||||||
class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraContainerState> {
|
class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraContainerState> {
|
||||||
final MeteringInteractor _meteringInteractor;
|
final MeteringInteractor _meteringInteractor;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||||
import 'package:lightmeter/data/models/film.dart';
|
import 'package:lightmeter/data/models/film.dart';
|
||||||
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
||||||
|
import 'package:lightmeter/features.dart';
|
||||||
import 'package:lightmeter/platform_config.dart';
|
import 'package:lightmeter/platform_config.dart';
|
||||||
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
@ -109,10 +110,7 @@ class CameraContainer extends StatelessWidget {
|
||||||
|
|
||||||
double _meteringContainerHeight(BuildContext context) {
|
double _meteringContainerHeight(BuildContext context) {
|
||||||
double enabledFeaturesHeight = 0;
|
double enabledFeaturesHeight = 0;
|
||||||
if (UserPreferencesProvider.meteringScreenFeatureOf(
|
if (FeaturesConfig.equipmentProfilesEnabled) {
|
||||||
context,
|
|
||||||
MeteringScreenLayoutFeature.equipmentProfiles,
|
|
||||||
)) {
|
|
||||||
enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight;
|
enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight;
|
||||||
enabledFeaturesHeight += Dimens.paddingS;
|
enabledFeaturesHeight += Dimens.paddingS;
|
||||||
}
|
}
|
||||||
|
@ -135,7 +133,7 @@ class CameraContainer extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
double _cameraPreviewHeight(BuildContext context) {
|
double _cameraPreviewHeight(BuildContext context) {
|
||||||
return ((MediaQuery.sizeOf(context).width - Dimens.grid8 - 2 * Dimens.paddingM) / 2) /
|
return ((MediaQuery.of(context).size.width - Dimens.grid8 - 2 * Dimens.paddingM) / 2) /
|
||||||
PlatformConfig.cameraPreviewAspectRatio;
|
PlatformConfig.cameraPreviewAspectRatio;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import 'package:lightmeter/screens/metering/communication/state_communication_me
|
||||||
import 'package:lightmeter/screens/metering/components/light_sensor_container/event_container_light_sensor.dart';
|
import 'package:lightmeter/screens/metering/components/light_sensor_container/event_container_light_sensor.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/light_sensor_container/state_container_light_sensor.dart';
|
import 'package:lightmeter/screens/metering/components/light_sensor_container/state_container_light_sensor.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/ev_source_base/bloc_base_ev_source.dart';
|
import 'package:lightmeter/screens/metering/components/shared/ev_source_base/bloc_base_ev_source.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:lightmeter/utils/log_2.dart';
|
||||||
|
|
||||||
class LightSensorContainerBloc
|
class LightSensorContainerBloc
|
||||||
extends EvSourceBlocBase<LightSensorContainerEvent, LightSensorContainerState> {
|
extends EvSourceBlocBase<LightSensorContainerEvent, LightSensorContainerState> {
|
||||||
|
|
|
@ -1,30 +1,24 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
|
||||||
class IconPlaceholder extends StatelessWidget {
|
class EmptyExposurePairsList extends StatelessWidget {
|
||||||
final IconData icon;
|
const EmptyExposurePairsList({super.key});
|
||||||
final String text;
|
|
||||||
|
|
||||||
const IconPlaceholder({
|
|
||||||
required this.icon,
|
|
||||||
required this.text,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
constraints: BoxConstraints(maxWidth: MediaQuery.sizeOf(context).width / 2),
|
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width / 2),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
icon,
|
Icons.not_interested,
|
||||||
color: Theme.of(context).colorScheme.onBackground,
|
color: Theme.of(context).colorScheme.onBackground,
|
||||||
),
|
),
|
||||||
const SizedBox(height: Dimens.grid8),
|
const SizedBox(height: Dimens.grid8),
|
||||||
Text(
|
Text(
|
||||||
text,
|
S.of(context).noExposurePairs,
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodyMedium
|
.bodyMedium
|
|
@ -1,10 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
|
||||||
|
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/components/empty_exposure_pairs_list/widget_list_exposure_pairs_empty.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/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';
|
|
||||||
|
|
||||||
class ExposurePairsList extends StatelessWidget {
|
class ExposurePairsList extends StatelessWidget {
|
||||||
final List<ExposurePair> exposurePairs;
|
final List<ExposurePair> exposurePairs;
|
||||||
|
@ -16,10 +15,7 @@ class ExposurePairsList extends StatelessWidget {
|
||||||
return AnimatedSwitcher(
|
return AnimatedSwitcher(
|
||||||
duration: Dimens.switchDuration,
|
duration: Dimens.switchDuration,
|
||||||
child: exposurePairs.isEmpty
|
child: exposurePairs.isEmpty
|
||||||
? IconPlaceholder(
|
? const EmptyExposurePairsList()
|
||||||
icon: Icons.not_interested,
|
|
||||||
text: S.of(context).noExposurePairs,
|
|
||||||
)
|
|
||||||
: Stack(
|
: Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
|
|
@ -2,12 +2,13 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||||
import 'package:lightmeter/data/models/film.dart';
|
import 'package:lightmeter/data/models/film.dart';
|
||||||
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
||||||
|
import 'package:lightmeter/features.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
|
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
||||||
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
||||||
import 'package:lightmeter/res/dimens.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/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/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';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
class ReadingsContainer extends StatelessWidget {
|
class ReadingsContainer extends StatelessWidget {
|
||||||
|
@ -37,10 +38,7 @@ class ReadingsContainer extends StatelessWidget {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
if (UserPreferencesProvider.meteringScreenFeatureOf(
|
if (FeaturesConfig.equipmentProfilesEnabled) ...[
|
||||||
context,
|
|
||||||
MeteringScreenLayoutFeature.equipmentProfiles,
|
|
||||||
)) ...[
|
|
||||||
const _EquipmentProfilePicker(),
|
const _EquipmentProfilePicker(),
|
||||||
const _InnerPadding(),
|
const _InnerPadding(),
|
||||||
],
|
],
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:lightmeter/data/models/ev_source_type.dart';
|
||||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||||
import 'package:lightmeter/data/models/film.dart';
|
import 'package:lightmeter/data/models/film.dart';
|
||||||
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
||||||
|
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
||||||
import 'package:lightmeter/providers/services_provider.dart';
|
import 'package:lightmeter/providers/services_provider.dart';
|
||||||
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
||||||
import 'package:lightmeter/screens/metering/bloc_metering.dart';
|
import 'package:lightmeter/screens/metering/bloc_metering.dart';
|
||||||
|
@ -16,7 +17,6 @@ import 'package:lightmeter/screens/metering/event_metering.dart';
|
||||||
import 'package:lightmeter/screens/metering/state_metering.dart';
|
import 'package:lightmeter/screens/metering/state_metering.dart';
|
||||||
import 'package:lightmeter/screens/metering/utils/listener_metering_layout_feature.dart';
|
import 'package:lightmeter/screens/metering/utils/listener_metering_layout_feature.dart';
|
||||||
import 'package:lightmeter/screens/metering/utils/listsner_equipment_profiles.dart';
|
import 'package:lightmeter/screens/metering/utils/listsner_equipment_profiles.dart';
|
||||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
class MeteringScreen extends StatelessWidget {
|
class MeteringScreen extends StatelessWidget {
|
||||||
|
@ -31,7 +31,7 @@ class MeteringScreen extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: BlocBuilder<MeteringBloc, MeteringState>(
|
child: BlocBuilder<MeteringBloc, MeteringState>(
|
||||||
builder: (_, state) => MeteringContainerBuidler(
|
builder: (_, state) => _MeteringContainerBuidler(
|
||||||
ev: state is MeteringDataState ? state.ev : null,
|
ev: state is MeteringDataState ? state.ev : null,
|
||||||
film: state.film,
|
film: state.film,
|
||||||
iso: state.iso,
|
iso: state.iso,
|
||||||
|
@ -80,25 +80,15 @@ class _InheritedListeners extends StatelessWidget {
|
||||||
child: MeteringScreenLayoutFeatureListener(
|
child: MeteringScreenLayoutFeatureListener(
|
||||||
feature: MeteringScreenLayoutFeature.filmPicker,
|
feature: MeteringScreenLayoutFeature.filmPicker,
|
||||||
onDidChangeDependencies: (value) {
|
onDidChangeDependencies: (value) {
|
||||||
if (!value) {
|
if (!value) context.read<MeteringBloc>().add(const FilmChangedEvent(Film.other()));
|
||||||
context.read<MeteringBloc>().add(const FilmChangedEvent(Film.other()));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
child: MeteringScreenLayoutFeatureListener(
|
child: child,
|
||||||
feature: MeteringScreenLayoutFeature.equipmentProfiles,
|
|
||||||
onDidChangeDependencies: (value) {
|
|
||||||
if (!value) {
|
|
||||||
EquipmentProfileProvider.of(context).setProfile(EquipmentProfiles.of(context).first);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MeteringContainerBuidler extends StatelessWidget {
|
class _MeteringContainerBuidler extends StatelessWidget {
|
||||||
final double? ev;
|
final double? ev;
|
||||||
final Film film;
|
final Film film;
|
||||||
final IsoValue iso;
|
final IsoValue iso;
|
||||||
|
@ -107,7 +97,7 @@ class MeteringContainerBuidler extends StatelessWidget {
|
||||||
final ValueChanged<IsoValue> onIsoChanged;
|
final ValueChanged<IsoValue> onIsoChanged;
|
||||||
final ValueChanged<NdValue> onNdChanged;
|
final ValueChanged<NdValue> onNdChanged;
|
||||||
|
|
||||||
const MeteringContainerBuidler({
|
const _MeteringContainerBuidler({
|
||||||
required this.ev,
|
required this.ev,
|
||||||
required this.film,
|
required this.film,
|
||||||
required this.iso,
|
required this.iso,
|
||||||
|
@ -119,14 +109,7 @@ class MeteringContainerBuidler extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final exposurePairs = ev != null
|
final exposurePairs = ev != null ? buildExposureValues(context, ev!, film) : <ExposurePair>[];
|
||||||
? buildExposureValues(
|
|
||||||
ev!,
|
|
||||||
UserPreferencesProvider.stopTypeOf(context),
|
|
||||||
EquipmentProfiles.selectedOf(context),
|
|
||||||
film,
|
|
||||||
)
|
|
||||||
: <ExposurePair>[];
|
|
||||||
final fastest = exposurePairs.isNotEmpty ? exposurePairs.first : null;
|
final fastest = exposurePairs.isNotEmpty ? exposurePairs.first : null;
|
||||||
final slowest = exposurePairs.isNotEmpty ? exposurePairs.last : null;
|
final slowest = exposurePairs.isNotEmpty ? exposurePairs.last : null;
|
||||||
// Doubled build here when switching evSourceType. As new source bloc fires a new state on init
|
// Doubled build here when switching evSourceType. As new source bloc fires a new state on init
|
||||||
|
@ -155,28 +138,39 @@ class MeteringContainerBuidler extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@visibleForTesting
|
List<ExposurePair> buildExposureValues(BuildContext context, double ev, Film film) {
|
||||||
static List<ExposurePair> buildExposureValues(
|
|
||||||
double ev,
|
|
||||||
StopType stopType,
|
|
||||||
EquipmentProfile equipmentProfile,
|
|
||||||
Film film,
|
|
||||||
) {
|
|
||||||
if (ev.isNaN || ev.isInfinite) {
|
if (ev.isNaN || ev.isInfinite) {
|
||||||
return List.empty();
|
return List.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Depending on the `stopType` the exposure pairs list length is multiplied by 1,2 or 3
|
/// Depending on the `stopType` the exposure pairs list length is multiplied by 1,2 or 3
|
||||||
|
final StopType stopType = UserPreferencesProvider.stopTypeOf(context);
|
||||||
final int evSteps = (ev * (stopType.index + 1)).round();
|
final int evSteps = (ev * (stopType.index + 1)).round();
|
||||||
|
|
||||||
final apertureValues = ApertureValue.values.whereStopType(stopType);
|
final EquipmentProfile equipmentProfile = EquipmentProfiles.selectedOf(context);
|
||||||
final shutterSpeedValues = ShutterSpeedValue.values.whereStopType(stopType);
|
final List<ApertureValue> apertureValues =
|
||||||
|
equipmentProfile.apertureValues.whereStopType(stopType);
|
||||||
|
final List<ShutterSpeedValue> shutterSpeedValues =
|
||||||
|
equipmentProfile.shutterSpeedValues.whereStopType(stopType);
|
||||||
|
|
||||||
/// Basically we use 1" shutter speed as an anchor point for building the exposure pairs list.
|
/// Basically we use 1" shutter speed as an anchor point for building the exposure pairs list.
|
||||||
/// But user can exclude this value from the list using custom equipment profile.
|
/// But user can exclude this value from the list using custom equipment profile.
|
||||||
/// So we have to restore the index of the anchor value.
|
/// So we have to restore the index of the anchor value.
|
||||||
const anchorShutterSpeed = ShutterSpeedValue(1, false, StopType.full);
|
const ShutterSpeedValue anchorShutterSpeed = ShutterSpeedValue(1, false, StopType.full);
|
||||||
final int anchorIndex = shutterSpeedValues.indexOf(anchorShutterSpeed);
|
int anchorIndex = shutterSpeedValues.indexOf(anchorShutterSpeed);
|
||||||
|
if (anchorIndex < 0) {
|
||||||
|
final filteredFullList = ShutterSpeedValue.values.whereStopType(stopType);
|
||||||
|
final customListStartIndex = filteredFullList.indexOf(shutterSpeedValues.first);
|
||||||
|
final fullListAnchor = filteredFullList.indexOf(anchorShutterSpeed);
|
||||||
|
if (customListStartIndex < fullListAnchor) {
|
||||||
|
/// This means, that user excluded anchor value at the end,
|
||||||
|
/// i.e. all shutter speed values are shorter than 1".
|
||||||
|
anchorIndex = fullListAnchor - customListStartIndex;
|
||||||
|
} else {
|
||||||
|
/// In case user excludes anchor value at the start,
|
||||||
|
/// we can do no adjustment.
|
||||||
|
}
|
||||||
|
}
|
||||||
final int evOffset = anchorIndex - evSteps;
|
final int evOffset = anchorIndex - evSteps;
|
||||||
|
|
||||||
late final int apertureOffset;
|
late final int apertureOffset;
|
||||||
|
@ -195,11 +189,10 @@ class MeteringContainerBuidler extends StatelessWidget {
|
||||||
) -
|
) -
|
||||||
max(apertureOffset, shutterSpeedOffset);
|
max(apertureOffset, shutterSpeedOffset);
|
||||||
|
|
||||||
if (itemsCount <= 0) {
|
if (itemsCount < 0) {
|
||||||
return List.empty();
|
return List.empty();
|
||||||
}
|
}
|
||||||
|
return List.generate(
|
||||||
final exposurePairs = List.generate(
|
|
||||||
itemsCount,
|
itemsCount,
|
||||||
(index) => ExposurePair(
|
(index) => ExposurePair(
|
||||||
apertureValues[index + apertureOffset],
|
apertureValues[index + apertureOffset],
|
||||||
|
@ -207,30 +200,5 @@ class MeteringContainerBuidler extends StatelessWidget {
|
||||||
),
|
),
|
||||||
growable: false,
|
growable: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Full equipment profile, nothing to cut
|
|
||||||
if (equipmentProfile.id == "") {
|
|
||||||
return exposurePairs;
|
|
||||||
}
|
|
||||||
|
|
||||||
final equipmentApertureValues = equipmentProfile.apertureValues.whereStopType(stopType);
|
|
||||||
final equipmentShutterSpeedValues = equipmentProfile.shutterSpeedValues.whereStopType(stopType);
|
|
||||||
|
|
||||||
final startCutEV = max(
|
|
||||||
exposurePairs.first.aperture.difference(equipmentApertureValues.first),
|
|
||||||
exposurePairs.first.shutterSpeed.difference(equipmentShutterSpeedValues.first),
|
|
||||||
);
|
|
||||||
final endCutEV = max(
|
|
||||||
equipmentApertureValues.last.difference(exposurePairs.last.aperture),
|
|
||||||
equipmentShutterSpeedValues.last.difference(exposurePairs.last.shutterSpeed),
|
|
||||||
);
|
|
||||||
|
|
||||||
final startCut = (startCutEV * (stopType.index + 1)).round().clamp(0, itemsCount);
|
|
||||||
final endCut = (endCutEV * (stopType.index + 1)).round().clamp(0, itemsCount);
|
|
||||||
|
|
||||||
if (startCut > itemsCount - endCut) {
|
|
||||||
return const [];
|
|
||||||
}
|
|
||||||
return exposurePairs.sublist(startCut, itemsCount - endCut);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<EquipmentProfile> onDidChangeDependencies;
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
const EquipmentProfileListener({
|
|
||||||
required this.onDidChangeDependencies,
|
|
||||||
required this.child,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<EquipmentProfileListener> createState() => _EquipmentProfileListenerState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _EquipmentProfileListenerState extends State<EquipmentProfileListener> {
|
|
||||||
@override
|
|
||||||
void didChangeDependencies() {
|
|
||||||
super.didChangeDependencies();
|
|
||||||
widget.onDidChangeDependencies(EquipmentProfiles.selectedOf(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return widget.child;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
class EquipmentProfileListener extends StatefulWidget {
|
class EquipmentProfileListener extends StatefulWidget {
|
||||||
|
|
|
@ -71,7 +71,6 @@ class EquipmentProfileContainerState extends State<EquipmentProfileContainer>
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
_AnimatedNameLeading(controller: _controller),
|
_AnimatedNameLeading(controller: _controller),
|
||||||
|
@ -164,7 +163,7 @@ class _AnimatedNameLeading extends AnimatedWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.only(right: _progress.value * Dimens.grid8),
|
padding: EdgeInsets.only(right: _progress.value * Dimens.grid24),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.edit,
|
Icons.edit,
|
||||||
size: _progress.value * Dimens.grid24,
|
size: _progress.value * Dimens.grid24,
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
|
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
||||||
|
|
||||||
import 'package:lightmeter/res/dimens.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_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/metering/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:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
||||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
class EquipmentProfilesScreen extends StatefulWidget {
|
class EquipmentProfilesScreen extends StatefulWidget {
|
||||||
|
@ -45,38 +44,30 @@ class _EquipmentProfilesScreenState extends State<EquipmentProfilesScreen> {
|
||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
slivers: profilesCount == 1
|
slivers: [
|
||||||
? [
|
SliverList(
|
||||||
SliverFillRemaining(
|
delegate: SliverChildBuilderDelegate(
|
||||||
hasScrollBody: false,
|
(context, index) => index > 0
|
||||||
child: _EquipmentProfilesListPlaceholder(onTap: _addProfile),
|
? Padding(
|
||||||
)
|
padding: EdgeInsets.fromLTRB(
|
||||||
]
|
Dimens.paddingM,
|
||||||
: [
|
index == 0 ? Dimens.paddingM : 0,
|
||||||
SliverList(
|
Dimens.paddingM,
|
||||||
delegate: SliverChildBuilderDelegate(
|
Dimens.paddingM,
|
||||||
(context, index) => index > 0 // skip default
|
),
|
||||||
? Padding(
|
child: EquipmentProfileContainer(
|
||||||
padding: EdgeInsets.fromLTRB(
|
key: profileContainersKeys[index],
|
||||||
Dimens.paddingM,
|
data: EquipmentProfiles.of(context)[index],
|
||||||
index == 0 ? Dimens.paddingM : 0,
|
onExpand: () => _keepExpandedAt(index),
|
||||||
Dimens.paddingM,
|
onUpdate: (profileData) => _updateProfileAt(profileData, index),
|
||||||
Dimens.paddingM,
|
onDelete: () => _removeProfileAt(index),
|
||||||
),
|
),
|
||||||
child: EquipmentProfileContainer(
|
)
|
||||||
key: profileContainersKeys[index],
|
: const SizedBox.shrink(),
|
||||||
data: EquipmentProfiles.of(context)[index],
|
childCount: profilesCount,
|
||||||
onExpand: () => _keepExpandedAt(index),
|
),
|
||||||
onUpdate: (profileData) => _updateProfileAt(profileData, index),
|
),
|
||||||
onDelete: () => _removeProfileAt(index),
|
],
|
||||||
),
|
|
||||||
)
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
childCount: profilesCount,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SliverToBoxAdapter(child: SizedBox(height: MediaQuery.paddingOf(context).bottom)),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,32 +99,3 @@ class _EquipmentProfilesScreenState extends State<EquipmentProfilesScreen> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EquipmentProfilesListPlaceholder extends StatelessWidget {
|
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
const _EquipmentProfilesListPlaceholder({required this.onTap});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: Dimens.sliverAppBarExpandedHeight),
|
|
||||||
child: FractionallySizedBox(
|
|
||||||
widthFactor: 1 / 1.618,
|
|
||||||
child: Center(
|
|
||||||
child: GestureDetector(
|
|
||||||
behavior: HitTestBehavior.translucent,
|
|
||||||
onTap: onTap,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(Dimens.paddingL),
|
|
||||||
child: IconPlaceholder(
|
|
||||||
icon: Icons.add,
|
|
||||||
text: S.of(context).tapToAdd,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/generated/l10n.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: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';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
class EquipmentProfilesListTile extends StatelessWidget {
|
class EquipmentProfilesListTile extends StatelessWidget {
|
||||||
|
@ -12,31 +8,13 @@ class EquipmentProfilesListTile extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final paidStatus = IAPProducts.productOf(context, IAPProductType.paidFeatures)?.status ??
|
|
||||||
IAPProductStatus.pending;
|
|
||||||
log(paidStatus.toString());
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const Icon(Icons.camera),
|
leading: const Icon(Icons.camera),
|
||||||
title: Text(S.of(context).equipmentProfiles),
|
title: Text(S.of(context).equipmentProfiles),
|
||||||
onTap: switch (paidStatus) {
|
onTap: () {
|
||||||
IAPProductStatus.purchased => () {
|
Navigator.of(context).push<EquipmentProfile>(
|
||||||
Navigator.of(context).push<EquipmentProfile>(
|
MaterialPageRoute(builder: (_) => const EquipmentProfilesScreen()),
|
||||||
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,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,10 +33,18 @@ class _MeteringScreenLayoutFeaturesDialogState extends State<MeteringScreenLayou
|
||||||
child: Text(S.of(context).meteringScreenLayoutHint),
|
child: Text(S.of(context).meteringScreenLayoutHint),
|
||||||
),
|
),
|
||||||
const SizedBox(height: Dimens.grid16),
|
const SizedBox(height: Dimens.grid16),
|
||||||
_featureListTile(MeteringScreenLayoutFeature.equipmentProfiles),
|
...MeteringScreenLayoutFeature.values.map(
|
||||||
_featureListTile(MeteringScreenLayoutFeature.extremeExposurePairs),
|
(f) => SwitchListTile(
|
||||||
_featureListTile(MeteringScreenLayoutFeature.filmPicker),
|
contentPadding: EdgeInsets.symmetric(horizontal: Dimens.dialogTitlePadding.left),
|
||||||
_featureListTile(MeteringScreenLayoutFeature.histogram),
|
title: Text(_toStringLocalized(context, f)),
|
||||||
|
value: _features[f]!,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_features.update(f, (_) => value);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -57,23 +65,8 @@ class _MeteringScreenLayoutFeaturesDialogState extends State<MeteringScreenLayou
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _featureListTile(MeteringScreenLayoutFeature f) {
|
|
||||||
return SwitchListTile(
|
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: Dimens.dialogTitlePadding.left),
|
|
||||||
title: Text(_toStringLocalized(context, f)),
|
|
||||||
value: _features[f]!,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_features.update(f, (_) => value);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _toStringLocalized(BuildContext context, MeteringScreenLayoutFeature feature) {
|
String _toStringLocalized(BuildContext context, MeteringScreenLayoutFeature feature) {
|
||||||
switch (feature) {
|
switch (feature) {
|
||||||
case MeteringScreenLayoutFeature.equipmentProfiles:
|
|
||||||
return S.of(context).equipmentProfiles;
|
|
||||||
case MeteringScreenLayoutFeature.extremeExposurePairs:
|
case MeteringScreenLayoutFeature.extremeExposurePairs:
|
||||||
return S.of(context).meteringScreenFeatureExtremeExposurePairs;
|
return S.of(context).meteringScreenFeatureExtremeExposurePairs;
|
||||||
case MeteringScreenLayoutFeature.filmPicker:
|
case MeteringScreenLayoutFeature.filmPicker:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/features.dart';
|
||||||
import 'package:lightmeter/generated/l10n.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/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/equipment_profiles/widget_list_tile_equipment_profiles.dart';
|
||||||
|
@ -17,7 +18,7 @@ class MeteringSettingsSection extends StatelessWidget {
|
||||||
StopTypeListTile(),
|
StopTypeListTile(),
|
||||||
CalibrationListTile(),
|
CalibrationListTile(),
|
||||||
MeteringScreenLayoutListTile(),
|
MeteringScreenLayoutListTile(),
|
||||||
EquipmentProfilesListTile(),
|
if (FeaturesConfig.equipmentProfilesEnabled) EquipmentProfilesListTile(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(child: SizedBox(height: MediaQuery.paddingOf(context).bottom)),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -24,7 +24,7 @@ class SliverScreen extends StatelessWidget {
|
||||||
SliverAppBar(
|
SliverAppBar(
|
||||||
pinned: true,
|
pinned: true,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
expandedHeight: Dimens.sliverAppBarExpandedHeight,
|
expandedHeight: Dimens.grid168,
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
titlePadding: const EdgeInsets.all(Dimens.paddingM),
|
titlePadding: const EdgeInsets.all(Dimens.paddingM),
|
||||||
|
@ -39,6 +39,7 @@ class SliverScreen extends StatelessWidget {
|
||||||
actions: appBarActions,
|
actions: appBarActions,
|
||||||
),
|
),
|
||||||
...slivers,
|
...slivers,
|
||||||
|
SliverToBoxAdapter(child: SizedBox(height: MediaQuery.of(context).padding.bottom)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
3
lib/utils/log_2.dart
Normal file
3
lib/utils/log_2.dart
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
double log2(num x) => log(x) / log(2);
|
24
pubspec.yaml
24
pubspec.yaml
|
@ -1,5 +1,5 @@
|
||||||
name: lightmeter
|
name: lightmeter
|
||||||
description: Lightmeter app inspired by Material 3 design system.
|
description: A new Flutter project.
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
version: 0.13.2+38
|
version: 0.13.2+38
|
||||||
|
|
||||||
|
@ -11,10 +11,10 @@ dependencies:
|
||||||
bloc_concurrency: 0.2.2
|
bloc_concurrency: 0.2.2
|
||||||
camera: 0.10.5+2
|
camera: 0.10.5+2
|
||||||
clipboard: 0.1.3
|
clipboard: 0.1.3
|
||||||
dynamic_color: 1.6.6
|
dynamic_color: 1.6.5
|
||||||
exif: 3.1.4
|
exif: 3.1.4
|
||||||
firebase_core: 2.14.0
|
firebase_core: 2.13.0
|
||||||
firebase_crashlytics: 3.3.3
|
firebase_crashlytics: 3.3.1
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_bloc: 8.1.3
|
flutter_bloc: 8.1.3
|
||||||
|
@ -23,26 +23,22 @@ dependencies:
|
||||||
intl: 0.18.0
|
intl: 0.18.0
|
||||||
intl_utils: 2.8.2
|
intl_utils: 2.8.2
|
||||||
light_sensor: 2.0.2
|
light_sensor: 2.0.2
|
||||||
m3_lightmeter_iap:
|
|
||||||
git:
|
|
||||||
url: "https://github.com/vodemn/m3_lightmeter_iap"
|
|
||||||
ref: main
|
|
||||||
m3_lightmeter_resources:
|
m3_lightmeter_resources:
|
||||||
git:
|
git:
|
||||||
url: "https://github.com/vodemn/m3_lightmeter_resources"
|
url: "https://github.com/vodemn/m3_lightmeter_resources"
|
||||||
ref: main
|
ref: main
|
||||||
material_color_utilities: 0.2.0
|
material_color_utilities: 0.2.0
|
||||||
package_info_plus: 4.0.2
|
package_info_plus: 4.0.1
|
||||||
permission_handler: 10.4.3
|
permission_handler: 10.2.0
|
||||||
platform: 3.1.0
|
platform: 3.1.0
|
||||||
shared_preferences: 2.2.0
|
shared_preferences: 2.1.1
|
||||||
url_launcher: 6.1.12
|
url_launcher: 6.1.11
|
||||||
uuid: 3.0.7
|
uuid: 3.0.7
|
||||||
vibration: 1.8.1
|
vibration: 1.7.7
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
bloc_test: 9.1.3
|
bloc_test: 9.1.3
|
||||||
build_runner: 2.4.6
|
build_runner: ^2.1.7
|
||||||
flutter_launcher_icons: 0.11.0
|
flutter_launcher_icons: 0.11.0
|
||||||
flutter_native_splash: 2.2.16
|
flutter_native_splash: 2.2.16
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -12,31 +12,12 @@ void main() {
|
||||||
'0': true,
|
'0': true,
|
||||||
'1': true,
|
'1': true,
|
||||||
'2': true,
|
'2': true,
|
||||||
'3': true,
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
MeteringScreenLayoutFeature.extremeExposurePairs: true,
|
MeteringScreenLayoutFeature.extremeExposurePairs: true,
|
||||||
MeteringScreenLayoutFeature.filmPicker: true,
|
MeteringScreenLayoutFeature.filmPicker: true,
|
||||||
MeteringScreenLayoutFeature.histogram: true,
|
MeteringScreenLayoutFeature.histogram: true,
|
||||||
MeteringScreenLayoutFeature.equipmentProfiles: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Legacy (no histogram & equipment profiles)', () {
|
|
||||||
expect(
|
|
||||||
MeteringScreenLayoutConfigJson.fromJson(
|
|
||||||
{
|
|
||||||
'0': false,
|
|
||||||
'1': false,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
{
|
|
||||||
MeteringScreenLayoutFeature.extremeExposurePairs: false,
|
|
||||||
MeteringScreenLayoutFeature.filmPicker: false,
|
|
||||||
MeteringScreenLayoutFeature.histogram: true,
|
|
||||||
MeteringScreenLayoutFeature.equipmentProfiles: true,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -45,32 +26,28 @@ void main() {
|
||||||
expect(
|
expect(
|
||||||
MeteringScreenLayoutConfigJson.fromJson(
|
MeteringScreenLayoutConfigJson.fromJson(
|
||||||
{
|
{
|
||||||
'0': false,
|
'0': true,
|
||||||
'1': false,
|
'1': true,
|
||||||
'2': false,
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
MeteringScreenLayoutFeature.extremeExposurePairs: false,
|
MeteringScreenLayoutFeature.extremeExposurePairs: true,
|
||||||
MeteringScreenLayoutFeature.filmPicker: false,
|
MeteringScreenLayoutFeature.filmPicker: true,
|
||||||
MeteringScreenLayoutFeature.histogram: false,
|
MeteringScreenLayoutFeature.histogram: true,
|
||||||
MeteringScreenLayoutFeature.equipmentProfiles: true,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
test('toJson()', () {
|
test('toJson', () {
|
||||||
expect(
|
expect(
|
||||||
{
|
{
|
||||||
MeteringScreenLayoutFeature.equipmentProfiles: true,
|
|
||||||
MeteringScreenLayoutFeature.extremeExposurePairs: true,
|
MeteringScreenLayoutFeature.extremeExposurePairs: true,
|
||||||
MeteringScreenLayoutFeature.filmPicker: true,
|
MeteringScreenLayoutFeature.filmPicker: true,
|
||||||
MeteringScreenLayoutFeature.histogram: true,
|
MeteringScreenLayoutFeature.histogram: true,
|
||||||
}.toJson(),
|
}.toJson(),
|
||||||
{
|
{
|
||||||
'3': true,
|
|
||||||
'0': true,
|
'0': true,
|
||||||
'1': true,
|
'1': true,
|
||||||
'2': true,
|
'2': true,
|
||||||
|
|
|
@ -193,7 +193,6 @@ void main() {
|
||||||
{
|
{
|
||||||
MeteringScreenLayoutFeature.extremeExposurePairs: true,
|
MeteringScreenLayoutFeature.extremeExposurePairs: true,
|
||||||
MeteringScreenLayoutFeature.filmPicker: true,
|
MeteringScreenLayoutFeature.filmPicker: true,
|
||||||
MeteringScreenLayoutFeature.equipmentProfiles: true,
|
|
||||||
MeteringScreenLayoutFeature.histogram: true,
|
MeteringScreenLayoutFeature.histogram: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -208,7 +207,6 @@ void main() {
|
||||||
{
|
{
|
||||||
MeteringScreenLayoutFeature.extremeExposurePairs: false,
|
MeteringScreenLayoutFeature.extremeExposurePairs: false,
|
||||||
MeteringScreenLayoutFeature.filmPicker: true,
|
MeteringScreenLayoutFeature.filmPicker: true,
|
||||||
MeteringScreenLayoutFeature.equipmentProfiles: true,
|
|
||||||
MeteringScreenLayoutFeature.histogram: true,
|
MeteringScreenLayoutFeature.histogram: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -218,19 +216,18 @@ void main() {
|
||||||
when(
|
when(
|
||||||
() => sharedPreferences.setString(
|
() => sharedPreferences.setString(
|
||||||
UserPreferencesService.meteringScreenLayoutKey,
|
UserPreferencesService.meteringScreenLayoutKey,
|
||||||
"""{"0":false,"1":true,"2":true,"3":true}""",
|
"""{"0":false,"1":true,"2":true}""",
|
||||||
),
|
),
|
||||||
).thenAnswer((_) => Future.value(true));
|
).thenAnswer((_) => Future.value(true));
|
||||||
service.meteringScreenLayout = {
|
service.meteringScreenLayout = {
|
||||||
MeteringScreenLayoutFeature.extremeExposurePairs: false,
|
MeteringScreenLayoutFeature.extremeExposurePairs: false,
|
||||||
MeteringScreenLayoutFeature.filmPicker: true,
|
MeteringScreenLayoutFeature.filmPicker: true,
|
||||||
MeteringScreenLayoutFeature.histogram: true,
|
MeteringScreenLayoutFeature.histogram: true,
|
||||||
MeteringScreenLayoutFeature.equipmentProfiles: true,
|
|
||||||
};
|
};
|
||||||
verify(
|
verify(
|
||||||
() => sharedPreferences.setString(
|
() => sharedPreferences.setString(
|
||||||
UserPreferencesService.meteringScreenLayoutKey,
|
UserPreferencesService.meteringScreenLayoutKey,
|
||||||
"""{"0":false,"1":true,"2":true,"3":true}""",
|
"""{"0":false,"1":true,"2":true}""",
|
||||||
),
|
),
|
||||||
).called(1);
|
).called(1);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,966 +0,0 @@
|
||||||
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';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
const defaultEquipmentProfile = EquipmentProfile(
|
|
||||||
id: "",
|
|
||||||
name: 'Default',
|
|
||||||
apertureValues: ApertureValue.values,
|
|
||||||
ndValues: NdValue.values,
|
|
||||||
shutterSpeedValues: ShutterSpeedValue.values,
|
|
||||||
isoValues: IsoValue.values,
|
|
||||||
);
|
|
||||||
|
|
||||||
group('Empty list', () {
|
|
||||||
List<ExposurePair> exposurePairsFull(double ev) => MeteringContainerBuidler.buildExposureValues(
|
|
||||||
ev,
|
|
||||||
StopType.full,
|
|
||||||
defaultEquipmentProfile,
|
|
||||||
const Film.other(),
|
|
||||||
);
|
|
||||||
|
|
||||||
test('isNan', () {
|
|
||||||
expect(exposurePairsFull(double.nan), const []);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('isInifinity', () {
|
|
||||||
expect(exposurePairsFull(double.infinity), const []);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Big ass number', () {
|
|
||||||
expect(exposurePairsFull(23), const []);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('Default equipment profile', () {
|
|
||||||
group("StopType.full", () {
|
|
||||||
List<ExposurePair> exposurePairsFull(double ev) =>
|
|
||||||
MeteringContainerBuidler.buildExposureValues(
|
|
||||||
ev,
|
|
||||||
StopType.full,
|
|
||||||
defaultEquipmentProfile,
|
|
||||||
const Film.other(),
|
|
||||||
);
|
|
||||||
|
|
||||||
test('EV 1', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(5.6, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.3', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.3);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(5.6, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.5', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.5);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(4, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(8, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.7', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.7);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(4, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(8, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 2', () {
|
|
||||||
final exposurePairs = exposurePairsFull(2);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(4, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(8, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group("StopType.half", () {
|
|
||||||
List<ExposurePair> exposurePairsFull(double ev) =>
|
|
||||||
MeteringContainerBuidler.buildExposureValues(
|
|
||||||
ev,
|
|
||||||
StopType.half,
|
|
||||||
defaultEquipmentProfile,
|
|
||||||
const Film.other(),
|
|
||||||
);
|
|
||||||
|
|
||||||
test('EV 1', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(5.6, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.3', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.3);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(3, true, StopType.half),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(6.7, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.5', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.5);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(3, true, StopType.half),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(6.7, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.7', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.7);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(3, true, StopType.half),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(6.7, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 2', () {
|
|
||||||
final exposurePairs = exposurePairsFull(2);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(4, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(8, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group("StopType.third", () {
|
|
||||||
List<ExposurePair> exposurePairsFull(double ev) =>
|
|
||||||
MeteringContainerBuidler.buildExposureValues(
|
|
||||||
ev,
|
|
||||||
StopType.third,
|
|
||||||
defaultEquipmentProfile,
|
|
||||||
const Film.other(),
|
|
||||||
);
|
|
||||||
|
|
||||||
test('EV 1', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(5.6, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.3', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.3);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(2.5, true, StopType.third),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(6.3, StopType.third),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.5', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.5);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(3, true, StopType.third),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(7.1, StopType.third),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.7', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.7);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(3, true, StopType.third),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(7.1, StopType.third),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 2', () {
|
|
||||||
final exposurePairs = exposurePairsFull(2);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1, StopType.full),
|
|
||||||
ShutterSpeedValue(4, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(8, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('Shutter speed 1/1000-1/2"', () {
|
|
||||||
final equipmentProfile = EquipmentProfile(
|
|
||||||
id: "1",
|
|
||||||
name: 'Test1',
|
|
||||||
apertureValues: ApertureValue.values,
|
|
||||||
ndValues: NdValue.values,
|
|
||||||
shutterSpeedValues: ShutterSpeedValue.values.sublist(
|
|
||||||
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(1000, true, StopType.full)),
|
|
||||||
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(2, true, StopType.full)) + 1,
|
|
||||||
),
|
|
||||||
isoValues: IsoValue.values,
|
|
||||||
);
|
|
||||||
|
|
||||||
group("StopType.full", () {
|
|
||||||
List<ExposurePair> exposurePairsFull(double ev) =>
|
|
||||||
MeteringContainerBuidler.buildExposureValues(
|
|
||||||
ev,
|
|
||||||
StopType.full,
|
|
||||||
equipmentProfile,
|
|
||||||
const Film.other(),
|
|
||||||
);
|
|
||||||
|
|
||||||
test('EV 1', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.3', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.3);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.5', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.5);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(4, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.4, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.7', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.7);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(4, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.4, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 2', () {
|
|
||||||
final exposurePairs = exposurePairsFull(2);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(4, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.4, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group("StopType.half", () {
|
|
||||||
List<ExposurePair> exposurePairsFull(double ev) =>
|
|
||||||
MeteringContainerBuidler.buildExposureValues(
|
|
||||||
ev,
|
|
||||||
StopType.half,
|
|
||||||
equipmentProfile,
|
|
||||||
const Film.other(),
|
|
||||||
);
|
|
||||||
|
|
||||||
test('EV 1', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.3', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.3);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(3, true, StopType.half),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.2, StopType.half),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.5', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.5);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(3, true, StopType.half),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.2, StopType.half),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.7', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.7);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(3, true, StopType.half),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.2, StopType.half),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 2', () {
|
|
||||||
final exposurePairs = exposurePairsFull(2);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(4, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.4, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group("StopType.third", () {
|
|
||||||
List<ExposurePair> exposurePairsFull(double ev) =>
|
|
||||||
MeteringContainerBuidler.buildExposureValues(
|
|
||||||
ev,
|
|
||||||
StopType.third,
|
|
||||||
equipmentProfile,
|
|
||||||
const Film.other(),
|
|
||||||
);
|
|
||||||
|
|
||||||
test('EV 1', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.3', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.3);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(2.5, true, StopType.third),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.1, StopType.third),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.5', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.5);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(3, true, StopType.third),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.2, StopType.third),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.7', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.7);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(3, true, StopType.third),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.2, StopType.third),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 2', () {
|
|
||||||
final exposurePairs = exposurePairsFull(2);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.0, StopType.full),
|
|
||||||
ShutterSpeedValue(4, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(1.4, StopType.full),
|
|
||||||
ShutterSpeedValue(2, true, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('Shutter speed 2"-16"', () {
|
|
||||||
final equipmentProfile = EquipmentProfile(
|
|
||||||
id: "1",
|
|
||||||
name: 'Test1',
|
|
||||||
apertureValues: ApertureValue.values.sublist(4),
|
|
||||||
ndValues: NdValue.values,
|
|
||||||
shutterSpeedValues: ShutterSpeedValue.values.sublist(
|
|
||||||
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(2, false, StopType.full)),
|
|
||||||
),
|
|
||||||
isoValues: IsoValue.values,
|
|
||||||
);
|
|
||||||
|
|
||||||
group("StopType.full", () {
|
|
||||||
List<ExposurePair> exposurePairsFull(double ev) =>
|
|
||||||
MeteringContainerBuidler.buildExposureValues(
|
|
||||||
ev,
|
|
||||||
StopType.full,
|
|
||||||
equipmentProfile,
|
|
||||||
const Film.other(),
|
|
||||||
);
|
|
||||||
|
|
||||||
test('EV 1', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.0, StopType.full),
|
|
||||||
ShutterSpeedValue(2, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(5.6, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.3', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.3);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.0, StopType.full),
|
|
||||||
ShutterSpeedValue(2, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(5.6, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.5', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.5);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.8, StopType.full),
|
|
||||||
ShutterSpeedValue(2, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(8, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.7', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.7);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.8, StopType.full),
|
|
||||||
ShutterSpeedValue(2, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(8, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 2', () {
|
|
||||||
final exposurePairs = exposurePairsFull(2);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.8, StopType.full),
|
|
||||||
ShutterSpeedValue(2, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(8, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group("StopType.half", () {
|
|
||||||
List<ExposurePair> exposurePairsFull(double ev) =>
|
|
||||||
MeteringContainerBuidler.buildExposureValues(
|
|
||||||
ev,
|
|
||||||
StopType.half,
|
|
||||||
equipmentProfile,
|
|
||||||
const Film.other(),
|
|
||||||
);
|
|
||||||
|
|
||||||
test('EV 1', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.0, StopType.full),
|
|
||||||
ShutterSpeedValue(2, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(5.6, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.3', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.3);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.4, StopType.half),
|
|
||||||
ShutterSpeedValue(2, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(6.7, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.5', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.5);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.4, StopType.half),
|
|
||||||
ShutterSpeedValue(2, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(6.7, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.7', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.7);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.4, StopType.half),
|
|
||||||
ShutterSpeedValue(2, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(6.7, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 2', () {
|
|
||||||
final exposurePairs = exposurePairsFull(2);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.8, StopType.full),
|
|
||||||
ShutterSpeedValue(2, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(8, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group("StopType.third", () {
|
|
||||||
List<ExposurePair> exposurePairsFull(double ev) =>
|
|
||||||
MeteringContainerBuidler.buildExposureValues(
|
|
||||||
ev,
|
|
||||||
StopType.third,
|
|
||||||
equipmentProfile,
|
|
||||||
const Film.other(),
|
|
||||||
);
|
|
||||||
|
|
||||||
test('EV 1', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.0, StopType.full),
|
|
||||||
ShutterSpeedValue(2, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(5.6, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.3', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.3);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.2, StopType.full),
|
|
||||||
ShutterSpeedValue(2, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(6.3, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.5', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.5);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.4, StopType.full),
|
|
||||||
ShutterSpeedValue(2, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(7.1, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 1.7', () {
|
|
||||||
final exposurePairs = exposurePairsFull(1.7);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.4, StopType.full),
|
|
||||||
ShutterSpeedValue(2, false, StopType.third),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(7.1, StopType.third),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('EV 2', () {
|
|
||||||
final exposurePairs = exposurePairsFull(2);
|
|
||||||
expect(
|
|
||||||
exposurePairs.first,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(2.8, StopType.full),
|
|
||||||
ShutterSpeedValue(2, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
exposurePairs.last,
|
|
||||||
const ExposurePair(
|
|
||||||
ApertureValue(8, StopType.full),
|
|
||||||
ShutterSpeedValue(16, false, StopType.full),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
Loading…
Reference in a new issue