Compare commits

..

No commits in common. "main" and "v0.1.0-alpha" have entirely different histories.

499 changed files with 2574 additions and 23655 deletions

3
.fvmrc
View file

@ -1,3 +0,0 @@
{
"flutter": "3.24.5"
}

3
.github/FUNDING.yml vendored
View file

@ -1,3 +0,0 @@
# These are supported funding model platforms
github: [vodemn]

View file

@ -1,30 +0,0 @@
---
name: Bug report
about: Create a bug report to help improve the app
title: ''
labels: bug, user feedback
assignees: vodemn
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Device:**
- Device: [e.g. Pixel 6]
- OS: [e.g. Android 12]
**App version**

View file

@ -1,17 +0,0 @@
---
name: Feature request or improvement
about: Suggest an idea for this project
title: ''
labels: feature, user feedback
assignees: vodemn
---
**Describe the feature or the problem it solves**
A clear and concise description of what the problem is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Additional context**
Add any other context or screenshots about the feature request here.

View file

@ -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

View file

@ -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
base64 -d <<< "$content" > "$filename"

View file

@ -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: v\d{1,2}.\d{1,2}.\d{1,2})/$1 path: iap/sg' pubspec.yaml

View file

@ -1,151 +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 Android
run-name: Build Android (${{ inputs.binary-type == 'apk' && '.apk' || '.aab' }}) v${{ inputs.version }}
on:
workflow_call:
inputs:
version:
description: "Version"
required: true
type: string
binary-type:
description: "Binary type"
type: string
required: true
flavor:
description: "Flavor"
type: string
required: true
include-iap:
type: boolean
description: Include IAP package
default: true
stage-backend:
type: boolean
description: Use stage backend
default: true
upload-artifact:
type: boolean
description: Upload build to artifacts
default: true
workflow_dispatch:
inputs:
version:
description: "Version"
required: true
type: string
binary-type:
description: "Binary type"
type: choice
required: true
options:
- apk
- appbundle
flavor:
description: "Flavor"
type: choice
required: true
options:
- dev
- prod
default: dev
include-iap:
type: boolean
description: Include IAP package
default: true
stage-backend:
type: boolean
description: Use stage backend
default: true
upload-artifact:
type: boolean
description: Upload build to artifacts
default: true
env:
BUILD_ARGS: --release --flavor ${{ inputs.flavor }} -t lib/main_${{ inputs.flavor }}.dart
BUILD_APK_PATH: build/app/outputs/flutter-apk/app-${{ inputs.flavor }}-release.apk
BUILD_AAB_PATH: build/app/outputs/bundle/${{ inputs.flavor }}Release/app-${{ inputs.flavor }}-release.aab
RELEASE_NOTES_ARTIFACT_NAME: release_notes_en_${{ inputs.version }}
RELEASE_NOTES_PATH: "assets/release_notes"
jobs:
build-android:
name: Build ${{ inputs.binary-type == 'apk' && '.apk' || '.aab' }}
runs-on: macos-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
with:
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
with:
distribution: "zulu"
java-version: "17"
- name: Restore Android keystore .jsk and .properties files
run: |
bash .github/scripts/restore_from_base64.sh "${{ secrets.KEYSTORE_PROPERTIES }}" "android/key.properties"
bash .github/scripts/restore_from_base64.sh "${{ secrets.KEYSTORE }}" "android/app/keystore.jks"
- name: Restore google-services.json
run: bash .github/scripts/restore_from_base64.sh "${{ secrets.GOOGLE_SERVICES_JSON_ANDROID }}" "android/app/google-services.json"
- name: Setup Firebase
run: |
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_OPTIONS }}" "lib/firebase_options.dart"
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_JSON }}" "firebase.json"
- name: Restore constants.dart
env:
CONSTANTS: ${{inputs.stage-backend && secrets.CONSTANTS_STAGE || secrets.CONSTANTS }}
run: bash .github/scripts/restore_from_base64.sh "${{ env.CONSTANTS }}" "lib/constants.dart"
- name: Increment build number & replace version number
run: bash ./.github/scripts/increment_build_number.sh ${{ github.event.inputs.version }}
- name: Download release notes
continue-on-error: true
uses: actions/download-artifact@v4
with:
name: ${{ env.RELEASE_NOTES_ARTIFACT_NAME }}
path: ${{ env.RELEASE_NOTES_PATH }}
- name: Install Flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.24.5"
- name: Prepare flutter project
run: |
flutter --version
flutter pub get
flutter pub run intl_utils:generate
- name: Build ${{ inputs.binary-type }}
run: flutter build ${{ inputs.binary-type }} $BUILD_ARGS
- name: Upload ${{ inputs.binary-type }} to artifacts
if: ${{ inputs.upload-artifact }}
uses: actions/upload-artifact@v4
with:
name: m3_lightmeter_${{ inputs.binary-type }}
path: ${{ inputs.binary-type == 'apk' && env.BUILD_APK_PATH || env.BUILD_AAB_PATH }}

View file

@ -1,156 +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 iOS
run-name: Build iOS v${{ inputs.version }}
on:
workflow_call:
inputs:
version:
description: "Version"
required: true
type: string
include-iap:
type: boolean
description: Include IAP package
default: true
stage-backend:
type: boolean
description: Use stage backend
default: true
upload-artifact:
type: boolean
description: Upload build to artifacts
default: true
workflow_dispatch:
inputs:
version:
description: "Version"
required: true
type: string
include-iap:
type: boolean
description: Include IAP package
default: true
stage-backend:
type: boolean
description: Use stage backend
default: true
upload-artifact:
type: boolean
description: Upload build to artifacts
default: true
env:
FLAVOR: "prod"
RELEASE_NOTES_ARTIFACT_NAME: release_notes_en_${{ inputs.version }}
RELEASE_NOTES_PATH: "assets/release_notes"
jobs:
build:
name: Build .ipa
runs-on: macos-latest
timeout-minutes: 60
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 }}
- name: Install the Apple certificate and provisioning profile
env:
APP_STORE_P12: ${{ secrets.APP_STORE_P12 }}
APP_STORE_P12_PASSWORD: ${{ secrets.APP_STORE_P12_PASSWORD }}
APP_STORE_PROVISION_PROD: ${{ secrets.APP_STORE_PROVISION_PROD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# create variables
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
PROVISION_PATH=$RUNNER_TEMP/build_provision.mobileprovision
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
# import certificate and provisioning profile from secrets
echo -n "$APP_STORE_P12" | base64 --decode -o $CERTIFICATE_PATH
echo -n "$APP_STORE_PROVISION_PROD" | base64 --decode -o $PROVISION_PATH
# create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# import certificate to keychain
security import $CERTIFICATE_PATH -P "$APP_STORE_P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
# apply provisioning profile
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PROVISION_PATH ~/Library/MobileDevice/Provisioning\ Profiles
- name: Restore GoogleService-Info.plist
run: bash .github/scripts/restore_from_base64.sh "${{ secrets.GOOGLE_SERVICES_JSON_IOS }}" "ios/Runner/GoogleService-Info.plist"
- name: Restore ExportOptions.plist
run: bash .github/scripts/restore_from_base64.sh "${{ secrets.APP_STORE_EXPORT_OPTIONS }}" "ios/Runner/ExportOptions.plist"
- name: Setup Firebase
run: |
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_APP_ID_FILE }}" "ios/firebase_app_id_file.json"
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_OPTIONS }}" "lib/firebase_options.dart"
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_JSON }}" "firebase.json"
- name: Restore constants.dart
env:
CONSTANTS: ${{inputs.stage-backend && secrets.CONSTANTS_STAGE || secrets.CONSTANTS }}
run: bash .github/scripts/restore_from_base64.sh "${{ env.CONSTANTS }}" "lib/constants.dart"
- name: Increment build number & replace version number
run: bash ./.github/scripts/increment_build_number.sh ${{ github.event.inputs.version }}
- name: Download release notes
continue-on-error: true
uses: actions/download-artifact@v4
with:
name: ${{ env.RELEASE_NOTES_ARTIFACT_NAME }}
path: ${{ env.RELEASE_NOTES_PATH }}
- name: Install Flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.24.5"
- name: Prepare flutter project
run: |
flutter --version
flutter pub get
flutter pub run intl_utils:generate
dart pub global activate flutterfire_cli
- name: Build .ipa
run: |
flutter build ipa \
--release \
--flavor $FLAVOR \
--target lib/main_$FLAVOR.dart \
--export-options-plist=ios/Runner/ExportOptions.plist
- name: Upload artifact
if: ${{ inputs.upload-artifact }}
uses: actions/upload-artifact@v4
with:
name: m3_lightmeter_ipa
path: build/ios/ipa/lightmeter.ipa
- name: Clean up keychain and provisioning profile
if: ${{ always() }}
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
rm ~/Library/MobileDevice/Provisioning\ Profiles/build_provision.mobileprovision

37
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,37 @@
# 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: Dart
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
channel: "stable"
- name: Check flutter version
run: flutter --version
- name: Install dependencies
run: flutter pub get
- name: Generate intl
run: flutter pub run intl_utils:generate
- name: Analyze project source
run: flutter analyze
- name: Run tests
run: flutter test

View file

@ -1,240 +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.
# This workflow uses perl regex. For better syntaxis understading see these docs:
# https://perldoc.perl.org/perlrequick#Search-and-replace
# https://perldoc.perl.org/perlre#Other-Modifiers
name: Create new release
run-name: Release v${{ inputs.version }}${{ inputs.release-track == 'beta' && '-Beta' || '' }}
on:
workflow_dispatch:
inputs:
version:
description: "Version"
required: true
type: string
release-notes:
description: "Release notes"
required: true
type: string
run-integration-tests:
description: "Run integration tests"
required: true
type: boolean
default: true
deploy-ios:
description: "Publish to App Store"
required: true
type: boolean
default: true
deploy-android:
description: "Publish to Google Play"
required: true
type: boolean
default: true
release-track:
description: "Release track"
type: choice
required: true
options:
- production
- beta
default: production
env:
RELEASE_NOTES_ARTIFACT_NAME: release_notes_en_${{ inputs.version }}
RELEASE_NOTES_FILE: release_notes_en_${{ inputs.version }}.md
RELEASE_NOTES_PATH: "assets/release_notes"
jobs:
run-integration-tests:
name: Run integration tests
if: ${{ inputs.run-integration-tests }}
uses: ./.github/workflows/run_integration_tests.yml
secrets: inherit
generate-release-notes:
name: Generate release notes
needs: [run-integration-tests]
if: ${{ always() && !failure() && !cancelled() }}
runs-on: ubuntu-latest
steps:
- name: Generate release notes
run: |
echo ${{ inputs.release-notes }} > ${{ env.RELEASE_NOTES_FILE }}
perl -i -pe 's/\s{1}(-{1})/\n$1/g' ${{ env.RELEASE_NOTES_FILE }}
- name: Upload merged_native_libs.zip to artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ env.RELEASE_NOTES_ARTIFACT_NAME }}
path: ${{ env.RELEASE_NOTES_FILE }}
build-android:
name: Build Android
needs: [generate-release-notes]
if: ${{ always() && !failure() && !cancelled() && inputs.deploy-android }}
strategy:
matrix:
binary-type: [apk, appbundle]
uses: ./.github/workflows/build_apk.yml
secrets: inherit
with:
binary-type: ${{ matrix.binary-type }}
flavor: prod
upload-artifact: true
stage-backend: false
version: ${{ inputs.version }}
build-ios:
name: Build iOS
needs: [generate-release-notes]
if: ${{ always() && !failure() && !cancelled() && inputs.deploy-ios }}
uses: ./.github/workflows/build_ipa.yml
secrets: inherit
with:
upload-artifact: true
stage-backend: false
version: ${{ inputs.version }}
create-github-release:
name: Create Github release
needs: [build-android, build-ios]
if: ${{ always() && !cancelled() && inputs.deploy-android}}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Download apk
uses: actions/download-artifact@v4
with:
name: m3_lightmeter_apk
- name: Download release notes
uses: actions/download-artifact@v4
with:
name: ${{ env.RELEASE_NOTES_ARTIFACT_NAME }}
path: ${{ env.RELEASE_NOTES_PATH }}
- name: Increment build number & replace version number
run: bash ./.github/scripts/increment_build_number.sh ${{ github.event.inputs.version }}
- name: Commit changes
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add --all -- ":!app-prod-release.apk"
git commit -m "Release v${{ inputs.version }}"
- name: Push to main
uses: CasperWA/push-protected@v2
with:
token: ${{ secrets.PUSH_TO_MAIN_TOKEN }}
branch: ${{ github.ref_name }}
unprotect_reviews: true
- name: Rename apk
run: mv app-prod-release.apk m3_lightmeter.apk
- uses: ncipollo/release-action@v1.12.0
with:
artifacts: "m3_lightmeter.apk"
skipIfReleaseExists: true
prerelease: ${{ inputs.release-track == 'beta' }}
tag: "v${{ github.event.inputs.version }}${{ inputs.release-track == 'beta' && '-beta' || '' }}"
bodyFile: "${{ env.RELEASE_NOTES_PATH }}/${{ env.RELEASE_NOTES_FILE }}"
create-google-play-release:
name: Create Google Play release
needs: [build-android, build-ios]
if: ${{ always() && !failure() && !cancelled() && inputs.deploy-android }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Download app bundle
uses: actions/download-artifact@v4
with:
name: m3_lightmeter_appbundle
- name: Extract & zip merged_native_libs
run: |
unzip app-prod-release.aab
(cd base/lib && zip -r "$OLDPWD/merged_native_libs.zip" .)
- name: Download release notes
uses: actions/download-artifact@v4
with:
name: ${{ env.RELEASE_NOTES_ARTIFACT_NAME }}
- name: Move release notes to a folder
run: |
mv ${{ env.RELEASE_NOTES_FILE }} whatsnew-en-US
mkdir whatsnew
mv whatsnew-en-US whatsnew
# https://unix.stackexchange.com/questions/13466/can-grep-output-only-specified-groupings-that-match'
# https://stackoverflow.com/questions/74353311/github-workflow-unable-to-process-file-command-env-successfully
- name: Create Google Play release name
id: release-name
run: |
RELEASE_NAME=$(echo "$(cat pubspec.yaml)" | sed -n -r "s/^version:\s{1}(.*)[+](.*)$/700\2 (\1)/p")
echo "release_name=$RELEASE_NAME" >> $GITHUB_ENV
- name: Create Google Play release
id: create-google-play-release-step
uses: r0adkll/upload-google-play@v1.1.1
with:
serviceAccountJsonPlainText: ${{ secrets.GH_ACTIONS_SERVICE_ACCOUNT_JSON }}
packageName: com.vodemn.lightmeter
releaseFiles: app-prod-release.aab
releaseName: ${{ env.release_name }}
track: ${{ inputs.release-track }}
status: completed
debugSymbols: merged_native_libs.zip
whatsNewDirectory: whatsnew
upload-to-app-store:
name: Upload to App Store
needs: [build-android, build-ios]
if: ${{ always() && !failure() && !cancelled() && inputs.deploy-ios }}
runs-on: macos-13
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Download ipa
uses: actions/download-artifact@v4
with:
name: m3_lightmeter_ipa
- name: Upload app to TestFlight
run: xcrun altool --upload-app -f lightmeter.ipa -t ios -u ${{ secrets.APP_STORE_USERNAME }} -p ${{ secrets.APP_STORE_PASSWORD }}
cleanup:
name: Cleanup
if: ${{ always() }}
needs:
[create-github-release, create-google-play-release, upload-to-app-store]
runs-on: ubuntu-latest
steps:
- name: Delete release artifacts
uses: geekyeggo/delete-artifact@v2
with:
failOnError: false
name: |
m3_lightmeter_apk
m3_lightmeter_appbundle
m3_lightmeter_ipa
${{ env.RELEASE_NOTES_ARTIFACT_NAME }}

View file

@ -1,33 +0,0 @@
name: Crowdin push
on:
workflow_dispatch:
push:
paths:
- lib/l10n/**
branches:
- main
- crowdin
concurrency:
group: ${{ github.workflow }}
permissions:
contents: read
env:
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
CROWDIN_PROJECT_ID: 567473
CROWDIN_TOKEN: ${{ secrets.CROWDIN_TOKEN }}
jobs:
upload-files:
runs-on: ubuntu-latest
steps:
- name: Checkout main branch
uses: actions/checkout@v3
- name: Upload
uses: crowdin/github-action@1.5.0
with:
crowdin_branch_name: main
upload_sources: true
upload_sources_args: "--delete-obsolete"
upload_translations: true
import_eq_suggestions: true
auto_approve_imported: false
add_crowdin_branch: main

View file

@ -1,120 +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: PR check
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
analyze-and-test:
name: Analyze & test
runs-on: macos-14
timeout-minutes: 10
env:
EMPTY_CONSTANTS: Y29uc3QgU3RyaW5nIGlhcFNlcnZlclVybCA9ICcnOwpjb25zdCBTdHJpbmcgaXNzdWVzUmVwb3J0VXJsID0gJyc7CmNvbnN0IFN0cmluZyBzb3VyY2VDb2RlVXJsID0gJyc7CmNvbnN0IFN0cmluZyBjb250YWN0RW1haWwgPSAnJzsK
EMPTY_FIREBASE_OPTIONS: Ly8gaWdub3JlX2Zvcl9maWxlOiB0eXBlPWxpbnQKaW1wb3J0ICdwYWNrYWdlOmZpcmViYXNlX2NvcmUvZmlyZWJhc2VfY29yZS5kYXJ0JyBzaG93IEZpcmViYXNlT3B0aW9uczsKY2xhc3MgRGVmYXVsdEZpcmViYXNlT3B0aW9ucyB7c3RhdGljIEZpcmViYXNlT3B0aW9ucyBnZXQgY3VycmVudFBsYXRmb3JtID0+RmlyZWJhc2VPcHRpb25zKGFwaUtleTogJycsIGFwcElkOiAnJywgbWVzc2FnaW5nU2VuZGVySWQ6ICcnLCBwcm9qZWN0SWQ6ICcnKTt9Cg==
steps:
- uses: 8BitJonny/gh-get-current-pr@2.2.0
id: PR
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Connect private iap package
uses: webfactory/ssh-agent@v0.8.0
id: fetch-iap
if: steps.PR.outputs.number == 'null' || 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
id: override-iap
if: steps.fetch-iap.conclusion != 'success'
run: bash ./.github/scripts/stub_iap.sh
- name: Restore secrets
env:
CONSTANTS: ${{ steps.fetch-iap.conclusion == 'success' && secrets.CONSTANTS || env.EMPTY_CONSTANTS }}
FIREBASE_OPTIONS: ${{ steps.fetch-iap.conclusion == 'success' && secrets.FIREBASE_OPTIONS || env.EMPTY_FIREBASE_OPTIONS }}
run: |
bash .github/scripts/restore_from_base64.sh "${{ env.CONSTANTS }}" "lib/constants.dart"
bash .github/scripts/restore_from_base64.sh "${{ env.FIREBASE_OPTIONS }}" "lib/firebase_options.dart"
- uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.24.5"
- name: Prepare flutter project
run: |
flutter --version
flutter pub get
flutter pub run intl_utils:generate
- name: Analyze project source
run: flutter analyze lib --fatal-infos
- name: Run tests
run: |
defaults -currentHost write -g AppleFontSmoothing -int 0
flutter test --dart-define cameraStubImage=assets/camera_stub_image.jpg
- name: Analyze project source with stub
if: steps.override-iap.conclusion != 'success'
run: |
bash ./.github/scripts/stub_iap.sh
flutter pub get
flutter analyze lib --fatal-infos
platform-changes:
name: Checks for platform changes
runs-on: ubuntu-latest
outputs:
android-changed: ${{ steps.platform-changes.outputs.android-changed }}
ios-changed: ${{ steps.platform-changes.outputs.ios-changed }}
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- id: platform-changes
uses: dorny/paths-filter@v2
with:
filters: |
android-changed:
- 'android/**'
- 'pubspec.yaml'
ios-changed:
- 'ios/**'
- 'pubspec.yaml'
build-android:
name: Build Android
needs: platform-changes
if: needs.platform-changes.outputs.android-changed == 'true'
uses: ./.github/workflows/build_apk.yml
secrets: inherit
with:
binary-type: apk
flavor: prod
upload-artifact: false
stage-backend: false
version: "1.0.0"
build-ios:
name: Build iOS
needs: platform-changes
if: needs.platform-changes.outputs.ios-changed == 'true'
uses: ./.github/workflows/build_ipa.yml
secrets: inherit
with:
upload-artifact: false
stage-backend: false
version: "1.0.0"

View file

@ -1,58 +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: Run integration tests
on:
workflow_dispatch:
workflow_call:
jobs:
run-integration-tests:
name: Run integration tests
timeout-minutes: 90
runs-on: macos-13
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Override iap package with stub
run: bash ./.github/scripts/stub_iap.sh
- name: Restore secrets
run: |
bash .github/scripts/restore_from_base64.sh "${{ secrets.CONSTANTS }}" "lib/constants.dart"
bash .github/scripts/restore_from_base64.sh "${{ secrets.GOOGLE_SERVICES_JSON_IOS }}" "ios/Runner/GoogleService-Info.plist"
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_APP_ID_FILE }}" "ios/firebase_app_id_file.json"
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_OPTIONS }}" "lib/firebase_options.dart"
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_JSON }}" "firebase.json"
- uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.24.5"
- name: Prepare app
run: |
flutter --version
flutter pub get
flutter pub run intl_utils:generate
flutter analyze lib --fatal-infos
dart pub global activate flutterfire_cli
- name: Launch iOS simulator
uses: futureware-tech/simulator-action@v3
with:
model: "iPhone 15 Pro"
- name: Run tests
run: |
flutter drive \
--target=integration_test/run_all_tests.dart \
--driver=test_driver/integration_driver.dart \
--flavor=dev \
--no-dds \
--dart-define cameraStubImage=assets/camera_stub_image.jpg

19
.gitignore vendored
View file

@ -5,11 +5,9 @@
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
@ -50,20 +48,5 @@ app.*.map.json
/ios/build/
/ios/Runner.xcodeproj/project.pbxproj
pubspec.lock
/ios/Podfile.lock
.fvm/
.jks
keystore.properties
android/app/google-services.json
ios/firebase_app_id_file.json
ios/Runner/GoogleService-Info.plist
lib/firebase_options.dart
lib/constants.dart
coverage/
test/coverage_helper_test.dart
**/failures/*.png
screenshots/generated/raw/
firebase.json

52
.vscode/launch.json vendored
View file

@ -5,74 +5,34 @@
"version": "0.2.0",
"configurations": [
{
"name": "dev-debug",
"name": "dev",
"request": "launch",
"type": "dart",
"flutterMode": "debug",
"args": [
"--flavor",
"dev",
],
"program": "${workspaceFolder}/lib/main_dev.dart",
"program": "${workspaceFolder}/lib/main.dart",
},
{
"name": "dev-profile",
"name": "dev (mock)",
"request": "launch",
"type": "dart",
"flutterMode": "profile",
"args": [
"--flavor",
"dev",
],
"program": "${workspaceFolder}/lib/main_dev.dart",
"program": "${workspaceFolder}/lib/main_mock.dart",
},
{
"name": "prod-debug",
"name": "prod",
"request": "launch",
"type": "dart",
"flutterMode": "debug",
"args": [
"--flavor",
"prod",
],
"program": "${workspaceFolder}/lib/main_prod.dart",
},
{
"name": "prod-profile",
"request": "launch",
"type": "dart",
"flutterMode": "profile",
"args": [
"--flavor",
"prod",
],
"program": "${workspaceFolder}/lib/main_prod.dart",
},
{
"name": "dev-simulator",
"request": "launch",
"type": "dart",
"flutterMode": "debug",
"args": [
"--flavor",
"dev",
"--dart-define",
"cameraStubImage=assets/camera_stub_image.jpg"
],
"program": "${workspaceFolder}/lib/main_dev.dart",
},
{
"name": "integration-test",
"request": "launch",
"type": "dart",
"flutterMode": "debug",
"args": [
"--flavor",
"dev",
"--dart-define",
"cameraStubImage=assets/camera_stub_image.jpg"
],
"program": "${workspaceFolder}/integration_test/run_all_tests.dart",
"program": "${workspaceFolder}/lib/main.dart",
},
],
}

25
.vscode/settings.json vendored
View file

@ -1,25 +0,0 @@
{
"task.slowProviderWarning": true,
"dart.flutterSdkPath": ".fvm/flutter_sdk",
"search.exclude": {
"**/.fvm": true
},
"files.watcherExclude": {
"**/.fvm": true
},
"dart.lineLength": 120,
"[dart]": {
"editor.rulers": [
120
],
"editor.selectionHighlight": true,
"editor.suggest.snippetsPreventQuickSuggestions": false,
"editor.suggestSelection": "first",
"editor.tabCompletion": "onlySnippets",
"editor.wordBasedSuggestions": "off"
},
"dart.doNotFormat": [
"**/generated/**",
"lib/data/**"
]
}

47
.vscode/tasks.json vendored
View file

@ -1,47 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "fvm_build_android_dev",
"type": "shell",
"command": ".fvm/flutter_sdk/bin/flutter",
"args": [
"build",
"apk",
"--flavor",
"dev",
"--release",
"-t",
"lib/main_dev.dart",
],
},
{
"label": "fvm_build_android_prod",
"type": "shell",
"command": ".fvm/flutter_sdk/bin/flutter",
"args": [
"build",
"apk",
"--flavor",
"prod",
"--release",
"-t",
"lib/main_prod.dart",
],
},
{
"label": "fvm_build_appbundle",
"type": "shell",
"command": ".fvm/flutter_sdk/bin/flutter",
"args": [
"build",
"appbundle",
"--flavor",
"prod",
"--release",
"-t",
"lib/main_prod.dart",
],
},
]
}

View file

@ -1,27 +0,0 @@
**Privacy Policy**
I, Vadim Turko, built the Material Lightmeter app as a Free app. This app is provided at no cost and is intended for use as is.
**Information Collection and Use**
When using this app no personal data is collected. All settings such as theme, calibration values and other preferences are stored only on your device and never sent over the Internet.
**Log Data**
I want to inform you that whenever you use Lightmeter app, in a case of an error in the app I collect data and information (through third-party products) on your phone called Log Data. This Log Data may include information such as your device name, operating system version, the configuration of the app, the time and date of your use of the App, and other statistics.
**Links to Other Sites**
This app contains links to other sites. If you click on a third-party link, you will be directed to that site. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.
* [GitHub](https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement)
**Changes to This Privacy Policy**
I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page.
This policy is effective as of 2024-01-04
**Contact Us**
If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at contact.vodemn@gmail.com.

View file

@ -1,83 +1 @@
<img src="resources/social_preview.png" width="100%" />
![](https://github.com/vodemn/m3_lightmeter/actions/workflows/pr_check.yml/badge.svg)
![](https://github.com/vodemn/m3_lightmeter/actions/workflows/create_release.yml/badge.svg)
# Table of contents
- [Table of contents](#table-of-contents)
- [Backstory](#backstory)
- [Screenshots](#screenshots)
- [Development](#development)
- [Support](#support)
- [iOS Limitations](#ios-limitations)
# Backstory
Some time ago I've started developing the [Material Lightmeter](https://play.google.com/store/apps/details?id=com.vodemn.lightmeter&hl=en&gl=US) app. Unfortunately, the last update of this app was almost a year prior to creation of this repo. So after reading some positive review on Google Play saying that "this is an excellent app, too bad it is no longer updated", I've decided to make an update and also make this app open source. Maybe someone sometime will decide to contribute to this project.
But as the existing repo contained some sensitive data, that I've pushed due to lack of experience, I had to make a new one. And if creating a new repo, why not rewrite the app from scratch?
Without further delay behold my new Lightmeter app inspired by Material You (a.k.a. M3)
# Screenshots
<p float="center">
<img src="screenshots/generated/ios/iphone65inch/light_metering-reflected.png" width="18.8%" />
<img src="screenshots/generated/ios/iphone65inch/light_equipment-profiles.png" width="18.8%" />
<img src="screenshots/generated/ios/iphone65inch/light_settings.png" width="18.8%" />
<img src="screenshots/generated/ios/iphone65inch/light_timer.png" width="18.8%" />
<img src="screenshots/generated/ios/iphone65inch/dark_metering-reflected.png" width="18.8%" />
</p>
# Development
### 1. Install Flutter
To build this app you need to install Flutter 3.24.5 stable. [How to install](https://docs.flutter.dev/get-started/install).
### 2. Project setup
#### Restore git-ignored files:
For macOS you can just run the following script:
```console
sh scripts/setup_fork.sh
```
Or create the files manually using the contents from the script.
#### Get dependencies
> If you are using VSCode, you can open the workspace like so: _File -> Open Workspace from File -> m3_lightmeter.code-workspace_. Otherwise you have to run `flutter pub get` command from the iap folder.
Then you can fetch all the neccessary dependencies and generate translation files by running the following commands:
```console
flutter pub get
flutter pub run intl_utils:generate
```
### 3. Build
- Checkout [Build .apk](.github/workflows/build_apk.yml) workflow for Android
- Checkout [Build .ipa](.github/workflows/build_ipa.yml) workflow for iOS
# Support
To report a bug or suggest a new feature open a new [issue](https://github.com/vodemn/m3_lightmeter/issues). To contribute to the project feel free to open a Pull Request, but you need to follow this [style guide](doc/style_guide.md).
In case you have any other questions please contact me via [email](mailto:contact.vodemn@gmail.com?subject="Lightmeter").
# iOS Limitations
A list of features, that Android version of the app has and that iOS does not.
## Incident light metering
Apple does not provide API for reading Lux stream form the ambient light sensor. Lux can be calculated based on front camera image stream, but this would be a reflected light. So there is no way incident light metering can be implemented on iOS.
## 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)
# material_lightmeter

View file

@ -1,10 +1 @@
include: package:lint/strict.yaml
linter:
rules:
use_setters_to_change_properties: false
analyzer:
exclude:
- "**/generated/**"
- lib/firebase_options.dart
include: package:flutter_lints/flutter.yaml

View file

@ -1,11 +1,3 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id "dev.flutter.flutter-gradle-plugin"
id "com.google.gms.google-services"
id "com.google.firebase.crashlytics"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
@ -14,6 +6,11 @@ if (localPropertiesFile.exists()) {
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
@ -24,15 +21,13 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 35
ndkVersion "27.0.12077973"
compileSdkVersion 33
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
@ -40,7 +35,7 @@ android {
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
jvmTarget = '1.8'
}
sourceSets {
@ -48,57 +43,25 @@ android {
}
defaultConfig {
minSdkVersion 23
targetSdkVersion 34
ndk {
debugSymbolLevel 'FULL'
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
/// legacy material-lightmeter ap stopped updating after 60022
/// 7xxxx means that it is a new app
versionCode 70000 + flutterVersionCode.toInteger()
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
flavorDimensions "app"
productFlavors {
dev {
resValue "string", "app_name", "Lightmeter (DEV)"
applicationId "com.vodemn.lightmeter.dev"
dimension "app"
signingConfig signingConfigs.release
applicationIdSuffix ".dev"
}
prod {
resValue "string", "app_name", "Lightmeter"
dimension "app"
signingConfig signingConfigs.release
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
signingConfig signingConfigs.release
minifyEnabled true
shrinkResources true
ndk {
debugSymbolLevel 'FULL'
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
prod {
applicationId "com.vodemn.lightmeter"
dimension "app"
signingConfig signingConfigs.debug
}
}
namespace 'com.vodemn.lightmeter'
}
flutter {
@ -106,6 +69,5 @@ flutter {
}
dependencies {
implementation "com.android.billingclient:billing-ktx:6.0.0"
implementation "com.google.firebase:firebase-analytics:17.4.1"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

View file

@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.vodemn.lightmeter">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#212121</color>
</resources>

View file

@ -1,11 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:label="@string/app_name"
package="com.vodemn.lightmeter">
<application
android:label="Lightmeter"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:allowBackup="false">
android:icon="@mipmap/launcher_icon">
<activity
android:name=".MainActivity"
android:exported="true"
@ -14,19 +12,18 @@
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:screenOrientation="portrait"
android:dataExtractionRules="@xml/data_extraction_rules"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
@ -36,18 +33,6 @@
android:value="2" />
</application>
<queries>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:scheme="mailto" />
<category android:name="android.intent.category.DEFAULT" />
</intent>
</queries>
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-permission android:name="android.permission.RECORD_AUDIO" tools:node="remove" />
<uses-permission android:name="android.permission.MICROPHONE" tools:node="remove" />
<uses-feature android:name="android.hardware.microphone" android:required="false" />
</manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View file

@ -1,95 +1,6 @@
package com.vodemn.lightmeter
import android.os.Bundle
import android.view.KeyEvent
import android.view.WindowManager
import androidx.core.view.WindowCompat
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.EventChannel.EventSink
import io.flutter.plugin.common.MethodChannel
class MainActivity : FlutterActivity() {
private lateinit var keepScreenOnChannel: MethodChannel
private lateinit var volumeHandlingChannel: MethodChannel
private lateinit var volumeEventChannel: EventChannel
private var volumeEventsEmitter: EventSink? = null
private var handleVolume = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
keepScreenOnChannel = MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"com.vodemn.lightmeter/keepScreenOn"
)
keepScreenOnChannel.setMethodCallHandler { call, result ->
when (call.method) {
"isKeepScreenOn" -> result.success((window.attributes.flags and WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0)
"setKeepScreenOn" -> {
if (call.arguments !is Boolean) {
result.error("invalid args", "Argument should be of type Bool for 'setKeepScreenOn' call", null)
} else {
if (call.arguments as Boolean) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
else window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
result.success(true)
}
}
else -> result.notImplemented()
}
}
volumeHandlingChannel = MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"com.vodemn.lightmeter/volumeHandling"
)
volumeHandlingChannel.setMethodCallHandler { call, result ->
when (call.method) {
"setVolumeHandling" -> {
handleVolume = call.arguments as Boolean
result.success(handleVolume)
}
else -> result.notImplemented()
}
}
volumeEventChannel = EventChannel(
flutterEngine.dartExecutor.binaryMessenger,
"com.vodemn.lightmeter/volumeEvents"
)
volumeEventChannel.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(listener: Any?, eventSink: EventSink) {
volumeEventsEmitter = eventSink
}
override fun onCancel(listener: Any?) {
volumeEventsEmitter = null
}
})
}
override fun onDestroy() {
keepScreenOnChannel.setMethodCallHandler(null)
volumeHandlingChannel.setMethodCallHandler(null)
volumeEventChannel.setStreamHandler(null)
super.onDestroy()
}
override fun onKeyDown(code: Int, event: KeyEvent): Boolean {
return when (val keyCode: Int = event.keyCode) {
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN -> {
if (handleVolume) {
volumeEventsEmitter?.success(keyCode)
true
} else {
super.onKeyDown(code, event)
}
}
else -> super.onKeyDown(code, event)
}
}
class MainActivity: FlutterActivity() {
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#212121</color>
</resources>

View file

@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 787 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#212121</color>
</resources>

View file

@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.vodemn.lightmeter">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.

View file

@ -1,3 +1,16 @@
buildscript {
ext.kotlin_version = '1.6.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
@ -13,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
task clean(type: Delete) {
delete rootProject.buildDir
}

View file

@ -1,6 +1,3 @@
org.gradle.jvmargs=-Xmx2048M
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false

View file

@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip

View file

@ -1,27 +1,11 @@
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
include ':app'
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version '8.7.3' apply false
id "org.jetbrains.kotlin.android" version "1.8.21" apply false
id "com.google.gms.google-services" version "4.4.0" apply false
id "com.google.firebase.crashlytics" version "2.9.9" apply false
}
include ":app"
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"

View file

@ -1,11 +0,0 @@
# Assets
## Launcher icons
### Android
Resources for Android are generated in Android Studio from the 512x512 source image as described in this [guide](https://developer.android.com/studio/write/create-app-icons).
### iOS
Resources for iOS are generated in XCode from the 1024x1024 source image as described in this [guide](https://developer.apple.com/documentation/xcode/configuring-your-app-icon).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 899 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -1,4 +0,0 @@
- [Pro] Added a timer for shooting long exposures without leaving the app, just tap the necessary shutter speed in the list.
- Long exposure values are no longer limited by 1" and are generated to match the list of aperture values.
- Refined the app's color palette & unified icons across the app.
- Added release notes dialog.

View file

@ -1,3 +0,0 @@
- Added ISO values up to 25600.
- Improved Pro features description.
- Made "Restore purchases" option more accessible.

View file

@ -1,4 +0,0 @@
- Added shutter speed values up to 1/4000.
- Added auto scaling for long values in the exposure pairs list.
- Fixed false purchasable status when offline.
- Fixed a delay when fetching product status on startup.

View file

@ -1,2 +0,0 @@
- [Pro] Added the ability to create custom films.
- [Pro] Added the ability to select equipment profiles in use.

View file

@ -1 +0,0 @@
- Performance improvements and bug fixes.

View file

@ -1 +0,0 @@
- Cleaned up camera preview implementation.

Some files were not shown because too many files have changed in this diff Show more