mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2025-07-05 13:40:41 +00:00
Compare commits
No commits in common. "1d2f858ea0951c79f60768fda6b8bb4a5f71696a" and "a42459de07627f0edfdda80c5bd013b50a9f1dc4" have entirely different histories.
1d2f858ea0
...
a42459de07
31 changed files with 212 additions and 327 deletions
14
.github/scripts/restore_from_base64.sh
vendored
14
.github/scripts/restore_from_base64.sh
vendored
|
@ -1,14 +0,0 @@
|
||||||
content="$1"
|
|
||||||
filename="$2"
|
|
||||||
|
|
||||||
if [[ ! -n "$content" ]]; then
|
|
||||||
echo "Provide file content"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -n "$filename" ]]; then
|
|
||||||
echo "Provide a path to an output file"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -n "$content" | base64 --decode --output "$filename"
|
|
72
.github/workflows/build_apk.yml
vendored
72
.github/workflows/build_apk.yml
vendored
|
@ -3,17 +3,27 @@
|
||||||
# separate terms of service, privacy policy, and support
|
# separate terms of service, privacy policy, and support
|
||||||
# documentation.
|
# documentation.
|
||||||
|
|
||||||
name: Build Prod .ipa
|
name: Build .apk
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
env:
|
flavor:
|
||||||
FLAVOR: "prod"
|
description: 'Flavor'
|
||||||
|
type: choice
|
||||||
|
required: true
|
||||||
|
options:
|
||||||
|
- dev
|
||||||
|
- prod
|
||||||
|
default: 'dev'
|
||||||
|
include-iap:
|
||||||
|
type: boolean
|
||||||
|
description: Include IAP package
|
||||||
|
default: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build .ipa
|
name: Build .apk
|
||||||
runs-on: macos-11
|
runs-on: macos-11
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
|
@ -23,34 +33,52 @@ jobs:
|
||||||
|
|
||||||
- name: Connect private iap package
|
- name: Connect private iap package
|
||||||
uses: webfactory/ssh-agent@v0.8.0
|
uses: webfactory/ssh-agent@v0.8.0
|
||||||
|
if: ${{ inputs.include-iap }}
|
||||||
with:
|
with:
|
||||||
ssh-private-key: ${{ secrets.M3_LIGHTMETER_IAP_KEY }}
|
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"
|
||||||
java-version: "11"
|
java-version: "11"
|
||||||
|
|
||||||
- name: Install Apple Certificate
|
- name: Restore Android keystore .jsk and .properties files
|
||||||
uses: apple-actions/import-codesign-certs@v1
|
env:
|
||||||
with:
|
KEYSTORE: ${{ secrets.KEYSTORE }}
|
||||||
p12-file-base64: ${{ secrets.APP_STORE_P12 }}
|
KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }}
|
||||||
p12-password: ${{ secrets.APP_STORE_P12_PASSWORD }}
|
|
||||||
- name: Install the provisioning profile
|
|
||||||
run: |
|
run: |
|
||||||
PROVISION_CERT_PATH=$RUNNER_TEMP/provision_prod.mobileprovision
|
KEYSTORE_PATH=$RUNNER_TEMP/keystore.jks
|
||||||
bash .github/scripts/restore_from_base64.sh "${{ secrets.APP_STORE_PROVISION_PROD }}" $PROVISION_CERT_PATH
|
echo -n "$KEYSTORE" | base64 --decode --output $KEYSTORE_PATH
|
||||||
mkdir -p ~/Library/MobileDevice\ Profiles
|
cp $KEYSTORE_PATH ./android/app
|
||||||
cp $PROVISION_CERT_PATH ~/Library/MobileDevice\ Profiles
|
KEYSTORE_PROPERTIES_PATH=$RUNNER_TEMP/key.properties
|
||||||
|
echo -n "$KEYSTORE_PROPERTIES" | base64 --decode --output $KEYSTORE_PROPERTIES_PATH
|
||||||
|
cp $KEYSTORE_PROPERTIES_PATH ./android
|
||||||
|
|
||||||
|
- name: Restore android/app/google-services.json
|
||||||
|
env:
|
||||||
|
GOOGLE_SERVICES_JSON_ANDROID: ${{ secrets.GOOGLE_SERVICES_JSON_ANDROID }}
|
||||||
|
run: |
|
||||||
|
GOOGLE_SERVICES_JSON_ANDROID_PATH=$RUNNER_TEMP/google-services.json
|
||||||
|
echo -n "$GOOGLE_SERVICES_JSON_ANDROID" | base64 --decode --output $GOOGLE_SERVICES_JSON_ANDROID_PATH
|
||||||
|
cp $GOOGLE_SERVICES_JSON_ANDROID_PATH ./android/app
|
||||||
|
|
||||||
- name: Restore firebase_options.dart
|
- name: Restore firebase_options.dart
|
||||||
run: bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_OPTIONS }}" "lib/firebase_options.dart"
|
env:
|
||||||
|
FIREBASE_OPTIONS: ${{ secrets.FIREBASE_OPTIONS }}
|
||||||
|
run: |
|
||||||
|
FIREBASE_OPTIONS_PATH=$RUNNER_TEMP/firebase_options.dart
|
||||||
|
echo -n "$FIREBASE_OPTIONS" | base64 --decode --output $FIREBASE_OPTIONS_PATH
|
||||||
|
cp $FIREBASE_OPTIONS_PATH ./lib
|
||||||
|
|
||||||
- name: Install Flutter
|
- name: Install Flutter
|
||||||
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: |
|
||||||
|
@ -58,11 +86,13 @@ jobs:
|
||||||
flutter pub get
|
flutter pub get
|
||||||
flutter pub run intl_utils:generate
|
flutter pub run intl_utils:generate
|
||||||
|
|
||||||
- name: Build .ipa
|
- name: Build .apk
|
||||||
run: flutter build ipa --release --flavor $FLAVOR -t lib/main_$FLAVOR.dart
|
env:
|
||||||
|
FLAVOR: ${{ github.event.inputs.flavor }}
|
||||||
|
run: flutter build apk --release --flavor $FLAVOR -t lib/main_$FLAVOR.dart
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: m3_lightmeter_$FLAVOR_ipa
|
name: m3_lightmeter_${{ github.event.inputs.flavor }}
|
||||||
path: build/ios/ipa/lightmeter.ipa
|
path: build/app/outputs/flutter-apk/app-${{ github.event.inputs.flavor }}-release.apk
|
||||||
|
|
68
.github/workflows/build_ipa.yml
vendored
68
.github/workflows/build_ipa.yml
vendored
|
@ -1,68 +0,0 @@
|
||||||
# This workflow uses actions that are not certified by GitHub.
|
|
||||||
# They are provided by a third-party and are governed by
|
|
||||||
# separate terms of service, privacy policy, and support
|
|
||||||
# documentation.
|
|
||||||
|
|
||||||
name: Build Prod .ipa
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
FLAVOR: "prod"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build .ipa
|
|
||||||
runs-on: macos-11
|
|
||||||
timeout-minutes: 15
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
|
|
||||||
- name: Connect private iap package
|
|
||||||
uses: webfactory/ssh-agent@v0.8.0
|
|
||||||
with:
|
|
||||||
ssh-private-key: ${{ secrets.M3_LIGHTMETER_IAP_KEY }}
|
|
||||||
|
|
||||||
- uses: actions/setup-java@v2
|
|
||||||
with:
|
|
||||||
distribution: "zulu"
|
|
||||||
java-version: "11"
|
|
||||||
|
|
||||||
- name: Install Apple Certificate
|
|
||||||
uses: apple-actions/import-codesign-certs@v1
|
|
||||||
with:
|
|
||||||
p12-file-base64: ${{ secrets.APP_STORE_P12 }}
|
|
||||||
p12-password: ${{ secrets.APP_STORE_P12_PASSWORD }}
|
|
||||||
- name: Install the provisioning profile
|
|
||||||
run: |
|
|
||||||
PROVISION_CERT_PATH=$RUNNER_TEMP/provision_prod.mobileprovision
|
|
||||||
bash .github/scripts/restore_from_base64.sh "${{ secrets.APP_STORE_PROVISION_PROD }}" $PROVISION_CERT_PATH
|
|
||||||
mkdir -p ~/Library/MobileDevice\ Profiles
|
|
||||||
cp $PROVISION_CERT_PATH ~/Library/MobileDevice\ Profiles
|
|
||||||
|
|
||||||
- name: Restore firebase_options.dart
|
|
||||||
run: bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_OPTIONS }}" "lib/firebase_options.dart"
|
|
||||||
|
|
||||||
- name: Install Flutter
|
|
||||||
uses: subosito/flutter-action@v2
|
|
||||||
with:
|
|
||||||
channel: "stable"
|
|
||||||
flutter-version: "3.10.0"
|
|
||||||
|
|
||||||
- name: Prepare flutter project
|
|
||||||
run: |
|
|
||||||
flutter --version
|
|
||||||
flutter pub get
|
|
||||||
flutter pub run intl_utils:generate
|
|
||||||
|
|
||||||
- name: Build .ipa
|
|
||||||
run: flutter build ipa --release --flavor $FLAVOR -t lib/main_$FLAVOR.dart
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: m3_lightmeter_$FLAVOR_ipa
|
|
||||||
path: build/ios/ipa/lightmeter.ipa
|
|
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
|
@ -35,7 +35,7 @@
|
||||||
"--flavor",
|
"--flavor",
|
||||||
"prod",
|
"prod",
|
||||||
],
|
],
|
||||||
"program": "${workspaceFolder}/lib/main_prod.dart",
|
"program": "${workspaceFolder}/lib/main_release.dart",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "prod-profile",
|
"name": "prod-profile",
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
"--flavor",
|
"--flavor",
|
||||||
"prod",
|
"prod",
|
||||||
],
|
],
|
||||||
"program": "${workspaceFolder}/lib/main_prod.dart",
|
"program": "${workspaceFolder}/lib/main_release.dart",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-simulator",
|
"name": "dev-simulator",
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<data-extraction-rules>
|
|
||||||
<cloud-backup>
|
|
||||||
<exclude domain="sharedpref" path="FlutterSecureStorage"/>
|
|
||||||
<exclude domain="sharedpref" path="FlutterSecureKeyStorage"/>
|
|
||||||
</cloud-backup>
|
|
||||||
<device-transfer>
|
|
||||||
<exclude domain="sharedpref" path="FlutterSecureStorage"/>
|
|
||||||
<exclude domain="sharedpref" path="FlutterSecureKeyStorage"/>
|
|
||||||
</device-transfer>
|
|
||||||
</data-extraction-rules>
|
|
|
@ -399,11 +399,9 @@
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = 489Z6UQMGN;
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 489Z6UQMGN;
|
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Lightmeter;
|
INFOPLIST_KEY_CFBundleDisplayName = Lightmeter;
|
||||||
|
@ -414,7 +412,6 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.vodemn.lightmeter;
|
PRODUCT_BUNDLE_IDENTIFIER = com.vodemn.lightmeter;
|
||||||
PRODUCT_NAME = Lightmeter;
|
PRODUCT_NAME = Lightmeter;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = m3_lightmeter_provision;
|
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
@ -536,11 +533,9 @@
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = 489Z6UQMGN;
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 489Z6UQMGN;
|
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Lightmeter;
|
INFOPLIST_KEY_CFBundleDisplayName = Lightmeter;
|
||||||
|
@ -551,7 +546,6 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.vodemn.lightmeter;
|
PRODUCT_BUNDLE_IDENTIFIER = com.vodemn.lightmeter;
|
||||||
PRODUCT_NAME = Lightmeter;
|
PRODUCT_NAME = Lightmeter;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = m3_lightmeter_provision;
|
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
@ -567,11 +561,9 @@
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = 489Z6UQMGN;
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 489Z6UQMGN;
|
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Lightmeter;
|
INFOPLIST_KEY_CFBundleDisplayName = Lightmeter;
|
||||||
|
@ -582,7 +574,6 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.vodemn.lightmeter;
|
PRODUCT_BUNDLE_IDENTIFIER = com.vodemn.lightmeter;
|
||||||
PRODUCT_NAME = Lightmeter;
|
PRODUCT_NAME = Lightmeter;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = m3_lightmeter_provision;
|
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
@ -652,11 +643,9 @@
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = 489Z6UQMGN;
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 489Z6UQMGN;
|
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Lightmeter (DEV)";
|
INFOPLIST_KEY_CFBundleDisplayName = "Lightmeter (DEV)";
|
||||||
|
@ -667,7 +656,6 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.vodemn.lightmeter.dev;
|
PRODUCT_BUNDLE_IDENTIFIER = com.vodemn.lightmeter.dev;
|
||||||
PRODUCT_NAME = "Lightmeter (DEV)";
|
PRODUCT_NAME = "Lightmeter (DEV)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = m3_lightmeter_provision;
|
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
@ -735,11 +723,9 @@
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = 489Z6UQMGN;
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 489Z6UQMGN;
|
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Lightmeter (DEV)";
|
INFOPLIST_KEY_CFBundleDisplayName = "Lightmeter (DEV)";
|
||||||
|
@ -750,7 +736,6 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.vodemn.lightmeter.dev;
|
PRODUCT_BUNDLE_IDENTIFIER = com.vodemn.lightmeter.dev;
|
||||||
PRODUCT_NAME = "Lightmeter (DEV)";
|
PRODUCT_NAME = "Lightmeter (DEV)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = m3_lightmeter_provision;
|
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
@ -815,11 +800,9 @@
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = 489Z6UQMGN;
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 489Z6UQMGN;
|
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Lightmeter (DEV)";
|
INFOPLIST_KEY_CFBundleDisplayName = "Lightmeter (DEV)";
|
||||||
|
@ -830,7 +813,6 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.vodemn.lightmeter.dev;
|
PRODUCT_BUNDLE_IDENTIFIER = com.vodemn.lightmeter.dev;
|
||||||
PRODUCT_NAME = "Lightmeter (DEV)";
|
PRODUCT_NAME = "Lightmeter (DEV)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = m3_lightmeter_provision;
|
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
|
|
@ -26,14 +26,11 @@ class ApplicationWrapper extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final remoteConfigService = env.buildType != BuildType.dev
|
|
||||||
? const RemoteConfigService(LightmeterAnalytics(api: LightmeterAnalyticsFirebase()))
|
|
||||||
: const MockRemoteConfigService();
|
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
future: Future.wait<dynamic>([
|
future: Future.wait<dynamic>([
|
||||||
SharedPreferences.getInstance(),
|
SharedPreferences.getInstance(),
|
||||||
const LightSensorService(LocalPlatform()).hasSensor(),
|
const LightSensorService(LocalPlatform()).hasSensor(),
|
||||||
remoteConfigService.activeAndFetchFeatures(),
|
if (env.buildType != BuildType.dev) const RemoteConfigService().activeAndFetchFeatures(),
|
||||||
]),
|
]),
|
||||||
builder: (_, snapshot) {
|
builder: (_, snapshot) {
|
||||||
if (snapshot.data != null) {
|
if (snapshot.data != null) {
|
||||||
|
@ -50,7 +47,8 @@ class ApplicationWrapper extends StatelessWidget {
|
||||||
userPreferencesService: userPreferencesService,
|
userPreferencesService: userPreferencesService,
|
||||||
volumeEventsService: const VolumeEventsService(LocalPlatform()),
|
volumeEventsService: const VolumeEventsService(LocalPlatform()),
|
||||||
child: RemoteConfigProvider(
|
child: RemoteConfigProvider(
|
||||||
remoteConfigService: remoteConfigService,
|
remoteConfigService:
|
||||||
|
env.buildType != BuildType.dev ? const RemoteConfigService() : const MockRemoteConfigService(),
|
||||||
child: EquipmentProfileProvider(
|
child: EquipmentProfileProvider(
|
||||||
storageService: iapService,
|
storageService: iapService,
|
||||||
child: FilmsProvider(
|
child: FilmsProvider(
|
||||||
|
|
|
@ -3,56 +3,32 @@ import 'dart:developer';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:lightmeter/data/analytics/api/analytics_api_interface.dart';
|
import 'package:lightmeter/data/analytics/api/analytics_api_interface.dart';
|
||||||
|
import 'package:lightmeter/data/analytics/entity/analytics_event.dart';
|
||||||
|
|
||||||
class LightmeterAnalytics {
|
class LightmeterAnalytics {
|
||||||
final ILightmeterAnalyticsApi _api;
|
final ILightmeterAnalyticsApi _api;
|
||||||
|
|
||||||
const LightmeterAnalytics({required ILightmeterAnalyticsApi api}) : _api = api;
|
const LightmeterAnalytics({required ILightmeterAnalyticsApi api}) : _api = api;
|
||||||
|
|
||||||
void init() {
|
|
||||||
FlutterError.onError = (details) {
|
|
||||||
if (details.silent) return;
|
|
||||||
logCrash(details.exception, details.stack);
|
|
||||||
};
|
|
||||||
PlatformDispatcher.instance.onError = (error, stack) {
|
|
||||||
logCrash(error, stack);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> logEvent(
|
Future<void> logEvent(
|
||||||
String eventName, {
|
LightmeterAnalyticsEvent event, {
|
||||||
Map<String, dynamic>? parameters,
|
Map<String, dynamic>? parameters,
|
||||||
}) async {
|
}) async {
|
||||||
if (!kReleaseMode) {
|
if (kDebugMode) {
|
||||||
log('<LightmeterAnalytics> logEvent: $eventName / $parameters');
|
log('<LightmeterAnalytics> logEvent: ${event.name} / $parameters');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _api.logEvent(
|
return _api.logEvent(
|
||||||
eventName,
|
event: event,
|
||||||
parameters: parameters,
|
parameters: parameters,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> logCrash(
|
Future<void> logUnlockProFeatures(String listTileTitle) async {
|
||||||
dynamic exception,
|
return logEvent(
|
||||||
StackTrace? stackTrace, {
|
LightmeterAnalyticsEvent.unlockProFeatures,
|
||||||
dynamic reason,
|
parameters: {"listTileTitle": listTileTitle},
|
||||||
Iterable<Object> information = const [],
|
|
||||||
}) async {
|
|
||||||
log(exception.toString(), stackTrace: stackTrace);
|
|
||||||
if (!kReleaseMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _api.logCrash(
|
|
||||||
exception,
|
|
||||||
stackTrace,
|
|
||||||
reason: reason,
|
|
||||||
information: information,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setCustomKey(String key, String value) async => _api.setCustomKey(key, value);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,8 @@
|
||||||
|
import 'package:lightmeter/data/analytics/entity/analytics_event.dart';
|
||||||
|
|
||||||
abstract class ILightmeterAnalyticsApi {
|
abstract class ILightmeterAnalyticsApi {
|
||||||
Future<void> logEvent(
|
Future<void> logEvent({
|
||||||
String eventName, {
|
required LightmeterAnalyticsEvent event,
|
||||||
Map<String, dynamic>? parameters,
|
Map<String, dynamic>? parameters,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<void> logCrash(
|
|
||||||
dynamic exception,
|
|
||||||
StackTrace? stack, {
|
|
||||||
dynamic reason,
|
|
||||||
Iterable<Object> information = const [],
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<void> setCustomKey(String key, String value);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import 'package:firebase_analytics/firebase_analytics.dart';
|
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:lightmeter/data/analytics/api/analytics_api_interface.dart';
|
import 'package:lightmeter/data/analytics/api/analytics_api_interface.dart';
|
||||||
|
import 'package:lightmeter/data/analytics/entity/analytics_event.dart';
|
||||||
|
|
||||||
class LightmeterAnalyticsFirebase implements ILightmeterAnalyticsApi {
|
class LightmeterAnalyticsFirebase implements ILightmeterAnalyticsApi {
|
||||||
const LightmeterAnalyticsFirebase();
|
const LightmeterAnalyticsFirebase();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> logEvent(
|
Future<void> logEvent({
|
||||||
String eventName, {
|
required LightmeterAnalyticsEvent event,
|
||||||
Map<String, dynamic>? parameters,
|
Map<String, dynamic>? parameters,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
await FirebaseAnalytics.instance.logEvent(
|
await FirebaseAnalytics.instance.logEvent(
|
||||||
name: eventName,
|
name: event.name,
|
||||||
parameters: parameters,
|
parameters: parameters,
|
||||||
);
|
);
|
||||||
} on FirebaseException catch (e) {
|
} on FirebaseException catch (e) {
|
||||||
|
@ -23,25 +23,4 @@ class LightmeterAnalyticsFirebase implements ILightmeterAnalyticsApi {
|
||||||
debugPrint(e.toString());
|
debugPrint(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> logCrash(
|
|
||||||
dynamic exception,
|
|
||||||
StackTrace? stackTrace, {
|
|
||||||
dynamic reason,
|
|
||||||
Iterable<Object> information = const [],
|
|
||||||
}) async {
|
|
||||||
FirebaseCrashlytics.instance.recordError(
|
|
||||||
exception,
|
|
||||||
stackTrace,
|
|
||||||
reason: reason,
|
|
||||||
information: information,
|
|
||||||
fatal: true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> setCustomKey(String key, String value) async {
|
|
||||||
await FirebaseCrashlytics.instance.setCustomKey(key, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
3
lib/data/analytics/entity/analytics_event.dart
Normal file
3
lib/data/analytics/entity/analytics_event.dart
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
enum LightmeterAnalyticsEvent {
|
||||||
|
unlockProFeatures,
|
||||||
|
}
|
|
@ -2,9 +2,9 @@ import 'dart:async';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:firebase_remote_config/firebase_remote_config.dart';
|
import 'package:firebase_remote_config/firebase_remote_config.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:lightmeter/data/analytics/analytics.dart';
|
|
||||||
import 'package:lightmeter/data/models/feature.dart';
|
import 'package:lightmeter/data/models/feature.dart';
|
||||||
|
|
||||||
abstract class IRemoteConfigService {
|
abstract class IRemoteConfigService {
|
||||||
|
@ -24,9 +24,7 @@ abstract class IRemoteConfigService {
|
||||||
}
|
}
|
||||||
|
|
||||||
class RemoteConfigService implements IRemoteConfigService {
|
class RemoteConfigService implements IRemoteConfigService {
|
||||||
final LightmeterAnalytics analytics;
|
const RemoteConfigService();
|
||||||
|
|
||||||
const RemoteConfigService(this.analytics);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> activeAndFetchFeatures() async {
|
Future<void> activeAndFetchFeatures() async {
|
||||||
|
@ -75,8 +73,8 @@ class RemoteConfigService implements IRemoteConfigService {
|
||||||
try {
|
try {
|
||||||
final feature = Feature.values.firstWhere((f) => f.name == value.key);
|
final feature = Feature.values.firstWhere((f) => f.name == value.key);
|
||||||
result[feature] = value.value.toValue(feature);
|
result[feature] = value.value.toValue(feature);
|
||||||
} catch (e, stackTrace) {
|
} catch (e) {
|
||||||
_logError(e, stackTrace: stackTrace);
|
log(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -90,8 +88,8 @@ class RemoteConfigService implements IRemoteConfigService {
|
||||||
for (final key in event.updatedKeys) {
|
for (final key in event.updatedKeys) {
|
||||||
try {
|
try {
|
||||||
updatedFeatures.add(Feature.values.firstWhere((element) => element.name == key));
|
updatedFeatures.add(Feature.values.firstWhere((element) => element.name == key));
|
||||||
} catch (e, stackTrace) {
|
} catch (e) {
|
||||||
_logError(e, stackTrace: stackTrace);
|
log(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return updatedFeatures;
|
return updatedFeatures;
|
||||||
|
@ -101,7 +99,9 @@ class RemoteConfigService implements IRemoteConfigService {
|
||||||
@override
|
@override
|
||||||
bool isEnabled(Feature feature) => FirebaseRemoteConfig.instance.getBool(feature.name);
|
bool isEnabled(Feature feature) => FirebaseRemoteConfig.instance.getBool(feature.name);
|
||||||
|
|
||||||
void _logError(dynamic throwable, {StackTrace? stackTrace}) => analytics.logCrash(throwable, stackTrace);
|
void _logError(dynamic throwable, {StackTrace? stackTrace}) {
|
||||||
|
FirebaseCrashlytics.instance.recordError(throwable, stackTrace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockRemoteConfigService implements IRemoteConfigService {
|
class MockRemoteConfigService implements IRemoteConfigService {
|
||||||
|
|
22
lib/firebase.dart
Normal file
22
lib/firebase.dart
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
import 'package:lightmeter/firebase_options.dart';
|
||||||
|
|
||||||
|
Future<void> initializeFirebase({required bool handleErrors}) async {
|
||||||
|
try {
|
||||||
|
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||||
|
if (handleErrors) {
|
||||||
|
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;
|
||||||
|
PlatformDispatcher.instance.onError = (error, stack) {
|
||||||
|
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log(e.toString());
|
||||||
|
}
|
||||||
|
}
|
|
@ -99,7 +99,7 @@
|
||||||
},
|
},
|
||||||
"proFeatures": "Pro features",
|
"proFeatures": "Pro features",
|
||||||
"unlockProFeatures": "Unlock Pro features",
|
"unlockProFeatures": "Unlock Pro features",
|
||||||
"unlockProFeaturesDescription": "Unlock professional features:\n \u2022 Equipment profiles containing filters for aperture, shutter speed, and more\n \u2022 List of films with compensation for what's known as reciprocity failure\n \u2022 Spot metering & Histogram\n \u2022 And more!\n\nBy unlocking Pro features you support the development and make it possible to add new features to the app.",
|
"unlockProFeaturesDescription": "Unlock professional features:\n \u2022 Equipment profiles containing filters for aperture, shutter speed, and more\n \u2022 List of films with compensation for what's known as reciprocity failure\n \u2022 Spot metering\n \u2022 Histogram\n\nBy unlocking Pro features you support the development and make it possible to add new features to the app.",
|
||||||
"unlock": "Unlock",
|
"unlock": "Unlock",
|
||||||
"tooltipAdd": "Add",
|
"tooltipAdd": "Add",
|
||||||
"tooltipClose": "Close",
|
"tooltipClose": "Close",
|
||||||
|
|
|
@ -99,7 +99,7 @@
|
||||||
},
|
},
|
||||||
"proFeatures": "Fonctionnalités professionnelles",
|
"proFeatures": "Fonctionnalités professionnelles",
|
||||||
"unlockProFeatures": "Déverrouiller les fonctionnalités professionnelles",
|
"unlockProFeatures": "Déverrouiller les fonctionnalités professionnelles",
|
||||||
"unlockProFeaturesDescription": "Déverrouillez des fonctions professionnelles:\n \u2022 Profils d'équipement contenant des filtres pour l'ouverture, la vitesse d'obturation et plus encore, ainsi qu'une liste de films avec compensation pour ce que l'on appelle l'échec de réciprocité\n \u2022 Mesure spot & Histogramme\n \u2022 Et plus encore!\n\nEn débloquant les fonctionnalités Pro, vous soutenez le développement et permettez d'ajouter de nouvelles fonctionnalités à l'application.",
|
"unlockProFeaturesDescription": "Déverrouillez des fonctions professionnelles:\n \u2022 Profils d'équipement contenant des filtres pour l'ouverture, la vitesse d'obturation et plus encore, ainsi qu'une liste de films avec compensation pour ce que l'on appelle l'échec de réciprocité\n \u2022 Mesure spot\n \u2022 Histogramme\n\nEn débloquant les fonctionnalités Pro, vous soutenez le développement et permettez d'ajouter de nouvelles fonctionnalités à l'application.",
|
||||||
"unlock": "Déverrouiller",
|
"unlock": "Déverrouiller",
|
||||||
"tooltipAdd": "Ajouter",
|
"tooltipAdd": "Ajouter",
|
||||||
"tooltipClose": "Fermer",
|
"tooltipClose": "Fermer",
|
||||||
|
|
|
@ -99,7 +99,7 @@
|
||||||
},
|
},
|
||||||
"proFeatures": "Профессиональные настройки",
|
"proFeatures": "Профессиональные настройки",
|
||||||
"unlockProFeatures": "Разблокировать профессиональные настройки",
|
"unlockProFeatures": "Разблокировать профессиональные настройки",
|
||||||
"unlockProFeaturesDescription": "Вы можете разблокировать профессиональные настройки:\n \u2022 Профили оборудования, содержащие фильтры для диафрагмы, выдержки и других значений\n \u2022 Список пленок с компенсацией эффекта Шварцшильда\n \u2022 Точечный замер и гистограмма\n \u2022 И другие возможности!\n\nПолучая доступ к профессиональным настройкам, вы поддерживаете разработку и делаете возможным появление новых функций в приложении.",
|
"unlockProFeaturesDescription": "Вы можете разблокировать профессиональные настройки:\n \u2022 Профили оборудования, содержащие фильтры для диафрагмы, выдержки и других значений\n \u2022 Список пленок с компенсацией эффекта Шварцшильда\n \u2022 Точечный замер\n \u2022 Гистограмма\n\nПолучая доступ к профессиональным настройкам, вы поддерживаете разработку и делаете возможным появление новых функций в приложении.",
|
||||||
"unlock": "Разблокировать",
|
"unlock": "Разблокировать",
|
||||||
"tooltipAdd": "Добавить",
|
"tooltipAdd": "Добавить",
|
||||||
"tooltipClose": "Закрыть",
|
"tooltipClose": "Закрыть",
|
||||||
|
|
|
@ -30,8 +30,8 @@
|
||||||
"halfStops": "1/2",
|
"halfStops": "1/2",
|
||||||
"thirdStops": "1/3",
|
"thirdStops": "1/3",
|
||||||
"calibration": "校准",
|
"calibration": "校准",
|
||||||
"calibrationMessage": "此应用程序测量读数的准确性取决于设备的后置摄像头。如需更精确的测量结果或测量结果存在偏差,请手动校准 EV 。",
|
"calibrationMessage": "此应用测量读数的准确性完全取决于设备的硬件。因此,请考虑测试此应用并手动设置 EV 校准,以获得准确的测量结果。",
|
||||||
"calibrationMessageCameraOnly": "此应用程序测量读数的准确性取决于设备的后置摄像头。如需更精确的测量结果或测量结果存在偏差,请手动校准 EV 。",
|
"calibrationMessageCameraOnly": "此应用程序测量读数的准确s性完全取决于设备的后置摄像头。因此,请考虑测试此应用并手动设置 EV 校准,以获得准确的测量结果。",
|
||||||
"camera": "摄像头",
|
"camera": "摄像头",
|
||||||
"lightSensor": "光传感器",
|
"lightSensor": "光传感器",
|
||||||
"showEv100": "显示 EV\u2081\u2080\u2080",
|
"showEv100": "显示 EV\u2081\u2080\u2080",
|
||||||
|
@ -55,14 +55,14 @@
|
||||||
"apertureValues": "光圈值",
|
"apertureValues": "光圈值",
|
||||||
"apertureValuesFilterDescription": "选择要显示的光圈值范围。这通常由您使用的镜头决定。",
|
"apertureValuesFilterDescription": "选择要显示的光圈值范围。这通常由您使用的镜头决定。",
|
||||||
"ndFilters": "ND 滤镜",
|
"ndFilters": "ND 滤镜",
|
||||||
"ndFiltersFilterDescription": "选择要显示的 ND 滤镜系数。可能是您最常用的 ND 滤镜,也可能是适合您镜头的减光镜。",
|
"ndFiltersFilterDescription": "选择要显示的 ND 滤镜系数。这些可能是您最常用的 ND 滤镜,也可能是适合您镜头的滤光镜。",
|
||||||
"shutterSpeedValues": "快门速度",
|
"shutterSpeedValues": "快门速度",
|
||||||
"shutterSpeedValuesFilterDescription": "选择要显示的快门速度范围。这通常由您使用的相机机身决定。",
|
"shutterSpeedValuesFilterDescription": "选择要显示的快门速度范围。这通常由您使用的相机机身决定。",
|
||||||
"isoValues": "ISO",
|
"isoValues": "ISO",
|
||||||
"isoValuesFilterDescription": "选择要显示的 ISO 。这些可能是您常用的ISO值,也可以是相机支持的ISO范围。",
|
"isoValuesFilterDescription": "选择要显示的 ISO 。这些值可能是您最常用的值,也可能是相机支持的值。",
|
||||||
"equipmentProfile": "设备配置",
|
"equipmentProfile": "设备配置",
|
||||||
"equipmentProfiles": "设备配置",
|
"equipmentProfiles": "设备配置",
|
||||||
"tapToAdd": "点击添加",
|
"tapToAdd": "點擊添加",
|
||||||
"filmsInUse": "使用的胶片",
|
"filmsInUse": "使用的胶片",
|
||||||
"filmsInUseDescription": "选择你使用的胶片",
|
"filmsInUseDescription": "选择你使用的胶片",
|
||||||
"general": "通用",
|
"general": "通用",
|
||||||
|
@ -99,7 +99,7 @@
|
||||||
},
|
},
|
||||||
"proFeatures": "专业功能",
|
"proFeatures": "专业功能",
|
||||||
"unlockProFeatures": "解锁专业功能",
|
"unlockProFeatures": "解锁专业功能",
|
||||||
"unlockProFeaturesDescription": "\n \u2022 配置文件,其中包含光圈、快门速度等参数\n \u2022 胶卷列表,对胶片倒易率失效进行曝光补偿\n \u2022 点测光和直方图\n \u2022 和更多\n\n通过解锁专业版功能,您可以支持开发工作,帮助为应用程序添加新功能。",
|
"unlockProFeaturesDescription": "\n \u2022 配置文件,其中包含光圈、快门速度等参数\n \u2022 胶卷列表,对胶片倒易率失效进行曝光补偿\n \u2022 点测光\n \u2022 直方图\n\n通过解锁专业版功能,您可以支持开发工作,帮助为应用程序添加新功能。",
|
||||||
"unlock": "解锁",
|
"unlock": "解锁",
|
||||||
"tooltipAdd": "添加",
|
"tooltipAdd": "添加",
|
||||||
"tooltipClose": "关闭",
|
"tooltipClose": "关闭",
|
||||||
|
|
|
@ -1,4 +1,18 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/application.dart';
|
||||||
|
import 'package:lightmeter/application_wrapper.dart';
|
||||||
import 'package:lightmeter/environment.dart';
|
import 'package:lightmeter/environment.dart';
|
||||||
import 'package:lightmeter/runner.dart';
|
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||||
|
|
||||||
Future<void> main() => runLightmeterApp(const Environment.dev());
|
Future<void> main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
runApp(
|
||||||
|
IAPProducts(
|
||||||
|
products: [IAPProduct(storeId: IAPProductType.paidFeatures.storeId)],
|
||||||
|
child: const ApplicationWrapper(
|
||||||
|
Environment.dev(),
|
||||||
|
child: Application(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,19 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/application.dart';
|
||||||
|
import 'package:lightmeter/application_wrapper.dart';
|
||||||
import 'package:lightmeter/environment.dart';
|
import 'package:lightmeter/environment.dart';
|
||||||
import 'package:lightmeter/runner.dart';
|
import 'package:lightmeter/firebase.dart';
|
||||||
|
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||||
|
|
||||||
Future<void> main() => runLightmeterApp(const Environment.prod());
|
Future<void> main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
await initializeFirebase(handleErrors: true);
|
||||||
|
runApp(
|
||||||
|
const IAPProductsProvider(
|
||||||
|
child: ApplicationWrapper(
|
||||||
|
Environment.prod(),
|
||||||
|
child: Application(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
19
lib/main_release.dart
Normal file
19
lib/main_release.dart
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/application.dart';
|
||||||
|
import 'package:lightmeter/application_wrapper.dart';
|
||||||
|
import 'package:lightmeter/environment.dart';
|
||||||
|
import 'package:lightmeter/firebase.dart';
|
||||||
|
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||||
|
|
||||||
|
Future<void> main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
await initializeFirebase(handleErrors: false);
|
||||||
|
runApp(
|
||||||
|
const IAPProductsProvider(
|
||||||
|
child: ApplicationWrapper(
|
||||||
|
Environment.prod(),
|
||||||
|
child: Application(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
|
@ -31,10 +31,8 @@ class ServicesProvider extends InheritedWidget {
|
||||||
required super.child,
|
required super.child,
|
||||||
});
|
});
|
||||||
|
|
||||||
static ServicesProvider of(BuildContext context) => ServicesProvider.maybeOf(context)!;
|
static ServicesProvider of(BuildContext context) {
|
||||||
|
return context.findAncestorWidgetOfExactType<ServicesProvider>()!;
|
||||||
static ServicesProvider? maybeOf(BuildContext context) {
|
|
||||||
return context.findAncestorWidgetOfExactType<ServicesProvider>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:lightmeter/application.dart';
|
|
||||||
import 'package:lightmeter/application_wrapper.dart';
|
|
||||||
import 'package:lightmeter/data/analytics/analytics.dart';
|
|
||||||
import 'package:lightmeter/data/analytics/api/analytics_firebase.dart';
|
|
||||||
import 'package:lightmeter/environment.dart';
|
|
||||||
import 'package:lightmeter/firebase_options.dart';
|
|
||||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
|
||||||
|
|
||||||
const _errorsLogger = LightmeterAnalytics(api: LightmeterAnalyticsFirebase());
|
|
||||||
|
|
||||||
Future<void> runLightmeterApp(Environment env) async {
|
|
||||||
runZonedGuarded(
|
|
||||||
() async {
|
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
|
||||||
if (env.buildType == BuildType.prod) {
|
|
||||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
|
||||||
}
|
|
||||||
_errorsLogger.init();
|
|
||||||
final application = ApplicationWrapper(env, child: const Application());
|
|
||||||
runApp(
|
|
||||||
env.buildType == BuildType.dev
|
|
||||||
? IAPProducts(
|
|
||||||
products: [IAPProduct(storeId: IAPProductType.paidFeatures.storeId)],
|
|
||||||
child: application,
|
|
||||||
)
|
|
||||||
: IAPProductsProvider(child: application),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
_errorsLogger.logCrash,
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -7,7 +7,6 @@ import 'package:camera/camera.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:lightmeter/data/analytics/analytics.dart';
|
|
||||||
import 'package:lightmeter/interactors/metering_interactor.dart';
|
import 'package:lightmeter/interactors/metering_interactor.dart';
|
||||||
import 'package:lightmeter/platform_config.dart';
|
import 'package:lightmeter/platform_config.dart';
|
||||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||||
|
@ -23,7 +22,6 @@ part 'mock_bloc_container_camera.dart';
|
||||||
|
|
||||||
class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraContainerState> {
|
class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraContainerState> {
|
||||||
final MeteringInteractor _meteringInteractor;
|
final MeteringInteractor _meteringInteractor;
|
||||||
final LightmeterAnalytics _analytics;
|
|
||||||
late final _WidgetsBindingObserver _observer;
|
late final _WidgetsBindingObserver _observer;
|
||||||
|
|
||||||
CameraController? _cameraController;
|
CameraController? _cameraController;
|
||||||
|
@ -44,7 +42,6 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
|
||||||
CameraContainerBloc(
|
CameraContainerBloc(
|
||||||
this._meteringInteractor,
|
this._meteringInteractor,
|
||||||
MeteringCommunicationBloc communicationBloc,
|
MeteringCommunicationBloc communicationBloc,
|
||||||
this._analytics,
|
|
||||||
) : super(
|
) : super(
|
||||||
communicationBloc,
|
communicationBloc,
|
||||||
const CameraInitState(),
|
const CameraInitState(),
|
||||||
|
@ -226,8 +223,8 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
|
||||||
Directory(file.path).deleteSync(recursive: true);
|
Directory(file.path).deleteSync(recursive: true);
|
||||||
|
|
||||||
return await evFromImage(bytes);
|
return await evFromImage(bytes);
|
||||||
} catch (e, stackTrace) {
|
} catch (e) {
|
||||||
_analytics.logCrash(e, stackTrace);
|
log(e.toString());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ class MockCameraContainerBloc extends CameraContainerBloc {
|
||||||
MockCameraContainerBloc(
|
MockCameraContainerBloc(
|
||||||
super._meteringInteractor,
|
super._meteringInteractor,
|
||||||
super.communicationBloc,
|
super.communicationBloc,
|
||||||
super._analytics,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -73,8 +72,8 @@ class MockCameraContainerBloc extends CameraContainerBloc {
|
||||||
try {
|
try {
|
||||||
final bytes = (await rootBundle.load(PlatformConfig.cameraStubImage)).buffer.asUint8List();
|
final bytes = (await rootBundle.load(PlatformConfig.cameraStubImage)).buffer.asUint8List();
|
||||||
return await evFromImage(bytes);
|
return await evFromImage(bytes);
|
||||||
} catch (e, stackTrace) {
|
} catch (e) {
|
||||||
log(e.toString(), stackTrace: stackTrace);
|
log(e.toString());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
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/platform_config.dart';
|
import 'package:lightmeter/platform_config.dart';
|
||||||
import 'package:lightmeter/providers/services_provider.dart';
|
|
||||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/camera_container/bloc_container_camera.dart';
|
import 'package:lightmeter/screens/metering/components/camera_container/bloc_container_camera.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/camera_container/event_container_camera.dart';
|
import 'package:lightmeter/screens/metering/components/camera_container/event_container_camera.dart';
|
||||||
|
@ -38,12 +37,10 @@ class CameraContainerProvider extends StatelessWidget {
|
||||||
? MockCameraContainerBloc(
|
? MockCameraContainerBloc(
|
||||||
MeteringInteractorProvider.of(context),
|
MeteringInteractorProvider.of(context),
|
||||||
context.read<MeteringCommunicationBloc>(),
|
context.read<MeteringCommunicationBloc>(),
|
||||||
ServicesProvider.of(context).analytics,
|
|
||||||
)
|
)
|
||||||
: CameraContainerBloc(
|
: CameraContainerBloc(
|
||||||
MeteringInteractorProvider.of(context),
|
MeteringInteractorProvider.of(context),
|
||||||
context.read<MeteringCommunicationBloc>(),
|
context.read<MeteringCommunicationBloc>(),
|
||||||
ServicesProvider.of(context).analytics,
|
|
||||||
))
|
))
|
||||||
..add(const RequestPermissionEvent()),
|
..add(const RequestPermissionEvent()),
|
||||||
child: CameraContainer(
|
child: CameraContainer(
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
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/services_provider.dart';
|
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/animated_dialog/widget_dialog_animated.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/animated_dialog/widget_dialog_animated.dart';
|
||||||
import 'package:lightmeter/screens/shared/transparent_dialog/widget_dialog_transparent.dart';
|
import 'package:lightmeter/screens/shared/transparent_dialog/widget_dialog_transparent.dart';
|
||||||
|
@ -45,12 +44,7 @@ class ProFeaturesDialog extends StatelessWidget {
|
||||||
),
|
),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_close(context).then((_) {
|
_close(context).then((_) => IAPProductsProvider.maybeOf(context)?.buy(IAPProductType.paidFeatures));
|
||||||
ServicesProvider.maybeOf(context)
|
|
||||||
?.analytics
|
|
||||||
.setCustomKey('iap_product_type', IAPProductType.paidFeatures.storeId);
|
|
||||||
IAPProductsProvider.maybeOf(context)?.buy(IAPProductType.paidFeatures);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
child: Text(S.of(context).unlock),
|
child: Text(S.of(context).unlock),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,20 +1,27 @@
|
||||||
|
import 'dart:developer';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:exif/exif.dart';
|
import 'package:exif/exif.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||||
|
|
||||||
Future<double> evFromImage(Uint8List bytes) async {
|
Future<double?> evFromImage(Uint8List bytes) async {
|
||||||
final tags = await readExifFromBytes(bytes);
|
try {
|
||||||
final iso = double.tryParse("${tags["EXIF ISOSpeedRatings"]}");
|
final tags = await readExifFromBytes(bytes);
|
||||||
final apertureValueRatio = (tags["EXIF FNumber"]?.values as IfdRatios?)?.ratios.first;
|
final iso = double.tryParse("${tags["EXIF ISOSpeedRatings"]}");
|
||||||
final speedValueRatio = (tags["EXIF ExposureTime"]?.values as IfdRatios?)?.ratios.first;
|
final apertureValueRatio = (tags["EXIF FNumber"]?.values as IfdRatios?)?.ratios.first;
|
||||||
if (iso == null || apertureValueRatio == null || speedValueRatio == null) {
|
final speedValueRatio = (tags["EXIF ExposureTime"]?.values as IfdRatios?)?.ratios.first;
|
||||||
throw FlutterError('Error parsing EXIF: ${tags.keys}');
|
if (iso == null || apertureValueRatio == null || speedValueRatio == null) {
|
||||||
|
log('Error parsing EXIF: ${tags.keys}');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final aperture = apertureValueRatio.numerator / apertureValueRatio.denominator;
|
||||||
|
final speed = speedValueRatio.numerator / speedValueRatio.denominator;
|
||||||
|
|
||||||
|
return log2(math.pow(aperture, 2)) - log2(speed) - log2(iso / 100);
|
||||||
|
} catch (e) {
|
||||||
|
log(e.toString());
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final aperture = apertureValueRatio.numerator / apertureValueRatio.denominator;
|
|
||||||
final speed = speedValueRatio.numerator / speedValueRatio.denominator;
|
|
||||||
|
|
||||||
return log2(math.pow(aperture, 2)) - log2(speed) - log2(iso / 100);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
name: lightmeter
|
name: lightmeter
|
||||||
description: Lightmeter app inspired by Material 3 design system.
|
description: Lightmeter app inspired by Material 3 design system.
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
version: 0.17.2+48
|
version: 0.17.0+46
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.0.0 <4.0.0"
|
sdk: ">=3.0.0 <4.0.0"
|
||||||
|
@ -28,7 +28,7 @@ dependencies:
|
||||||
m3_lightmeter_iap:
|
m3_lightmeter_iap:
|
||||||
git:
|
git:
|
||||||
url: "https://github.com/vodemn/m3_lightmeter_iap"
|
url: "https://github.com/vodemn/m3_lightmeter_iap"
|
||||||
ref: v0.7.2
|
ref: v0.7.1
|
||||||
m3_lightmeter_resources:
|
m3_lightmeter_resources:
|
||||||
git:
|
git:
|
||||||
url: "https://github.com/vodemn/m3_lightmeter_resources"
|
url: "https://github.com/vodemn/m3_lightmeter_resources"
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'package:bloc_test/bloc_test.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:lightmeter/data/analytics/analytics.dart';
|
|
||||||
import 'package:lightmeter/interactors/metering_interactor.dart';
|
import 'package:lightmeter/interactors/metering_interactor.dart';
|
||||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||||
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' as communication_events;
|
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' as communication_events;
|
||||||
|
@ -19,14 +18,11 @@ class _MockMeteringCommunicationBloc
|
||||||
extends MockBloc<communication_events.MeteringCommunicationEvent, communication_states.MeteringCommunicationState>
|
extends MockBloc<communication_events.MeteringCommunicationEvent, communication_states.MeteringCommunicationState>
|
||||||
implements MeteringCommunicationBloc {}
|
implements MeteringCommunicationBloc {}
|
||||||
|
|
||||||
class _MockLightmeterAnalytics extends Mock implements LightmeterAnalytics {}
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
TestWidgetsFlutterBinding.ensureInitialized();
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
late _MockMeteringInteractor meteringInteractor;
|
late _MockMeteringInteractor meteringInteractor;
|
||||||
late _MockMeteringCommunicationBloc communicationBloc;
|
late _MockMeteringCommunicationBloc communicationBloc;
|
||||||
late _MockLightmeterAnalytics analytics;
|
|
||||||
late CameraContainerBloc bloc;
|
late CameraContainerBloc bloc;
|
||||||
|
|
||||||
const cameraMethodChannel = MethodChannel('plugins.flutter.io/camera');
|
const cameraMethodChannel = MethodChannel('plugins.flutter.io/camera');
|
||||||
|
@ -114,21 +110,16 @@ void main() {
|
||||||
|
|
||||||
setUpAll(() {
|
setUpAll(() {
|
||||||
meteringInteractor = _MockMeteringInteractor();
|
meteringInteractor = _MockMeteringInteractor();
|
||||||
|
|
||||||
communicationBloc = _MockMeteringCommunicationBloc();
|
communicationBloc = _MockMeteringCommunicationBloc();
|
||||||
|
|
||||||
when(() => meteringInteractor.cameraEvCalibration).thenReturn(0.0);
|
when(() => meteringInteractor.cameraEvCalibration).thenReturn(0.0);
|
||||||
when(meteringInteractor.quickVibration).thenAnswer((_) async {});
|
when(meteringInteractor.quickVibration).thenAnswer((_) async {});
|
||||||
|
|
||||||
analytics = _MockLightmeterAnalytics();
|
|
||||||
registerFallbackValue(StackTrace.empty);
|
|
||||||
when(() => analytics.logCrash(any<dynamic>(), any<StackTrace>())).thenAnswer((_) async {});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
bloc = CameraContainerBloc(
|
bloc = CameraContainerBloc(
|
||||||
meteringInteractor,
|
meteringInteractor,
|
||||||
communicationBloc,
|
communicationBloc,
|
||||||
analytics,
|
|
||||||
);
|
);
|
||||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
.setMockMethodCallHandler(cameraMethodChannel, cameraMethodCallSuccessHandler);
|
.setMockMethodCallHandler(cameraMethodChannel, cameraMethodCallSuccessHandler);
|
||||||
|
|
|
@ -17,7 +17,7 @@ void main() {
|
||||||
'no EXIF',
|
'no EXIF',
|
||||||
() {
|
() {
|
||||||
final bytes = File('assets/launcher_icon_dev_512.png').readAsBytesSync();
|
final bytes = File('assets/launcher_icon_dev_512.png').readAsBytesSync();
|
||||||
expectLater(evFromImage(bytes), throwsFlutterError);
|
expectLater(evFromImage(bytes), completion(null));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue