Compare commits
No commits in common. "main" and "v0.1.0-alpha" have entirely different histories.
main
...
v0.1.0-alp
3
.fvmrc
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"flutter": "3.24.5"
|
|
||||||
}
|
|
3
.github/FUNDING.yml
vendored
|
@ -1,3 +0,0 @@
|
||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: [vodemn]
|
|
30
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -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**
|
|
|
@ -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.
|
|
8
.github/scripts/increment_build_number.sh
vendored
|
@ -1,8 +0,0 @@
|
||||||
export newVersion="$1"
|
|
||||||
|
|
||||||
if [[ -n "$newVersion" ]]; then
|
|
||||||
#https://stackoverflow.com/a/30214769/13167574
|
|
||||||
perl -i -pe 's/^(version:\s+)(\d+\.\d+\.\d+)(\+)(\d+)$/$1.$ENV{'newVersion'}.$3.($4+1)/e' pubspec.yaml
|
|
||||||
else
|
|
||||||
echo "argument error"
|
|
||||||
fi
|
|
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
|
|
||||||
|
|
||||||
base64 -d <<< "$content" > "$filename"
|
|
2
.github/scripts/stub_iap.sh
vendored
|
@ -1,2 +0,0 @@
|
||||||
# https://unix.stackexchange.com/questions/435708/regex-multiline-pattern-and-substitution-replacement
|
|
||||||
perl -0777 -i -pe 's/( m3_lightmeter_iap:\n)( git:\n url: "https:\/\/github.com\/vodemn\/m3_lightmeter_iap"\n ref: v\d{1,2}.\d{1,2}.\d{1,2})/$1 path: iap/sg' pubspec.yaml
|
|
151
.github/workflows/build_apk.yml
vendored
|
@ -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 }}
|
|
156
.github/workflows/build_ipa.yml
vendored
|
@ -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
|
@ -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
|
240
.github/workflows/create_release.yml
vendored
|
@ -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 }}
|
|
33
.github/workflows/crowdin_push.yml
vendored
|
@ -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
|
|
120
.github/workflows/pr_check.yml
vendored
|
@ -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"
|
|
58
.github/workflows/run_integration_tests.yml
vendored
|
@ -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
|
@ -5,11 +5,9 @@
|
||||||
*.swp
|
*.swp
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.atom/
|
.atom/
|
||||||
.build/
|
|
||||||
.buildlog/
|
.buildlog/
|
||||||
.history
|
.history
|
||||||
.svn/
|
.svn/
|
||||||
.swiftpm/
|
|
||||||
migrate_working_dir/
|
migrate_working_dir/
|
||||||
|
|
||||||
# IntelliJ related
|
# IntelliJ related
|
||||||
|
@ -50,20 +48,5 @@ app.*.map.json
|
||||||
/ios/build/
|
/ios/build/
|
||||||
/ios/Runner.xcodeproj/project.pbxproj
|
/ios/Runner.xcodeproj/project.pbxproj
|
||||||
|
|
||||||
|
pubspec.lock
|
||||||
/ios/Podfile.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
|
@ -5,74 +5,34 @@
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "dev-debug",
|
"name": "dev",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"flutterMode": "debug",
|
|
||||||
"args": [
|
"args": [
|
||||||
"--flavor",
|
"--flavor",
|
||||||
"dev",
|
"dev",
|
||||||
],
|
],
|
||||||
"program": "${workspaceFolder}/lib/main_dev.dart",
|
"program": "${workspaceFolder}/lib/main.dart",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dev-profile",
|
"name": "dev (mock)",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"flutterMode": "profile",
|
|
||||||
"args": [
|
"args": [
|
||||||
"--flavor",
|
"--flavor",
|
||||||
"dev",
|
"dev",
|
||||||
],
|
],
|
||||||
"program": "${workspaceFolder}/lib/main_dev.dart",
|
"program": "${workspaceFolder}/lib/main_mock.dart",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "prod-debug",
|
"name": "prod",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"flutterMode": "debug",
|
|
||||||
"args": [
|
"args": [
|
||||||
"--flavor",
|
"--flavor",
|
||||||
"prod",
|
"prod",
|
||||||
],
|
],
|
||||||
"program": "${workspaceFolder}/lib/main_prod.dart",
|
"program": "${workspaceFolder}/lib/main.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",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
25
.vscode/settings.json
vendored
|
@ -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
|
@ -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",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -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.
|
|
84
README.md
|
@ -1,83 +1 @@
|
||||||
<img src="resources/social_preview.png" width="100%" />
|
# material_lightmeter
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
# 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)
|
|
|
@ -1,10 +1 @@
|
||||||
include: package:lint/strict.yaml
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
linter:
|
|
||||||
rules:
|
|
||||||
use_setters_to_change_properties: false
|
|
||||||
|
|
||||||
analyzer:
|
|
||||||
exclude:
|
|
||||||
- "**/generated/**"
|
|
||||||
- lib/firebase_options.dart
|
|
||||||
|
|
|
@ -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 localProperties = new Properties()
|
||||||
def localPropertiesFile = rootProject.file('local.properties')
|
def localPropertiesFile = rootProject.file('local.properties')
|
||||||
if (localPropertiesFile.exists()) {
|
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')
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
if (flutterVersionCode == null) {
|
if (flutterVersionCode == null) {
|
||||||
flutterVersionCode = '1'
|
flutterVersionCode = '1'
|
||||||
|
@ -24,15 +21,13 @@ if (flutterVersionName == null) {
|
||||||
flutterVersionName = '1.0'
|
flutterVersionName = '1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
def keystoreProperties = new Properties()
|
apply plugin: 'com.android.application'
|
||||||
def keystorePropertiesFile = rootProject.file('key.properties')
|
apply plugin: 'kotlin-android'
|
||||||
if (keystorePropertiesFile.exists()) {
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 35
|
compileSdkVersion 33
|
||||||
ndkVersion "27.0.12077973"
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
@ -40,7 +35,7 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = JavaVersion.VERSION_1_8
|
jvmTarget = '1.8'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
@ -48,57 +43,25 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 23
|
minSdkVersion 21
|
||||||
targetSdkVersion 34
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
ndk {
|
versionCode flutterVersionCode.toInteger()
|
||||||
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()
|
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
|
||||||
release {
|
|
||||||
keyAlias keystoreProperties['keyAlias']
|
|
||||||
keyPassword keystoreProperties['keyPassword']
|
|
||||||
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
|
||||||
storePassword keystoreProperties['storePassword']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
flavorDimensions "app"
|
flavorDimensions "app"
|
||||||
productFlavors {
|
productFlavors {
|
||||||
dev {
|
dev {
|
||||||
resValue "string", "app_name", "Lightmeter (DEV)"
|
applicationId "com.vodemn.lightmeter.dev"
|
||||||
dimension "app"
|
dimension "app"
|
||||||
signingConfig signingConfigs.release
|
|
||||||
applicationIdSuffix ".dev"
|
|
||||||
}
|
|
||||||
prod {
|
|
||||||
resValue "string", "app_name", "Lightmeter"
|
|
||||||
dimension "app"
|
|
||||||
signingConfig signingConfigs.release
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
debug {
|
|
||||||
signingConfig signingConfigs.debug
|
signingConfig signingConfigs.debug
|
||||||
}
|
}
|
||||||
release {
|
prod {
|
||||||
signingConfig signingConfigs.release
|
applicationId "com.vodemn.lightmeter"
|
||||||
minifyEnabled true
|
dimension "app"
|
||||||
shrinkResources true
|
signingConfig signingConfigs.debug
|
||||||
ndk {
|
|
||||||
debugSymbolLevel 'FULL'
|
|
||||||
abiFilters 'armeabi-v7a', 'arm64-v8a'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
namespace 'com.vodemn.lightmeter'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
flutter {
|
flutter {
|
||||||
|
@ -106,6 +69,5 @@ flutter {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "com.android.billingclient:billing-ktx:6.0.0"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation "com.google.firebase:firebase-analytics:17.4.1"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 INTERNET permission is required for development. Specifically,
|
||||||
the Flutter tool needs it to communicate with the running application
|
the Flutter tool needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
|
Before Width: | Height: | Size: 24 KiB |
|
@ -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>
|
|
|
@ -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>
|
|
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB |
|
@ -1,4 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<color name="ic_launcher_background">#212121</color>
|
|
||||||
</resources>
|
|
|
@ -1,11 +1,9 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
package="com.vodemn.lightmeter">
|
||||||
|
<application
|
||||||
<application
|
android:label="Lightmeter"
|
||||||
android:label="@string/app_name"
|
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/launcher_icon">
|
||||||
android:allowBackup="false">
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
@ -14,19 +12,18 @@
|
||||||
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
|
||||||
while the Flutter UI initializes. After that, this theme continues
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
to determine the Window background behind the Flutter UI. -->
|
to determine the Window background behind the Flutter UI. -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.flutter.embedding.android.NormalTheme"
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
android:resource="@style/NormalTheme"
|
android:resource="@style/NormalTheme"
|
||||||
/>
|
/>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
|
@ -36,18 +33,6 @@
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
</application>
|
</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-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="true" />
|
<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>
|
</manifest>
|
Before Width: | Height: | Size: 18 KiB |
|
@ -1,95 +1,6 @@
|
||||||
package com.vodemn.lightmeter
|
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.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() {
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 27 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 544 B |
BIN
android/app/src/main/res/mipmap-hdpi/launcher_icon.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 442 B |
BIN
android/app/src/main/res/mipmap-mdpi/launcher_icon.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 721 B |
BIN
android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
|
@ -1,4 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<color name="ic_launcher_background">#212121</color>
|
|
||||||
</resources>
|
|
|
@ -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>
|
|
Before Width: | Height: | Size: 15 KiB |
|
@ -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>
|
|
|
@ -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>
|
|
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 787 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 10 KiB |
|
@ -1,4 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<color name="ic_launcher_background">#212121</color>
|
|
||||||
</resources>
|
|
|
@ -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 INTERNET permission is required for development. Specifically,
|
||||||
the Flutter tool needs it to communicate with the running application
|
the Flutter tool needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
|
|
@ -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 {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
@ -13,6 +26,6 @@ subprojects {
|
||||||
project.evaluationDependsOn(':app')
|
project.evaluationDependsOn(':app')
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register("clean", Delete) {
|
task clean(type: Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
org.gradle.jvmargs=-Xmx2048M
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
android.defaults.buildfeatures.buildconfig=true
|
|
||||||
android.nonTransitiveRClass=false
|
|
||||||
android.nonFinalResIds=false
|
|
||||||
|
|
|
@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
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
|
||||||
|
|
|
@ -1,27 +1,11 @@
|
||||||
pluginManagement {
|
include ':app'
|
||||||
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
|
|
||||||
}()
|
|
||||||
|
|
||||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||||
|
def properties = new Properties()
|
||||||
|
|
||||||
repositories {
|
assert localPropertiesFile.exists()
|
||||||
google()
|
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||||
mavenCentral()
|
|
||||||
gradlePluginPortal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins {
|
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||||
id "com.android.application" version '8.7.3' apply false
|
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
||||||
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"
|
|
||||||
|
|
|
@ -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).
|
|
Before Width: | Height: | Size: 899 KiB |
BIN
assets/launcher_icon_circle.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 6 KiB |
BIN
assets/launcher_icon_square.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
|
@ -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.
|
|
|
@ -1,3 +0,0 @@
|
||||||
- Added ISO values up to 25600.
|
|
||||||
- Improved Pro features description.
|
|
||||||
- Made "Restore purchases" option more accessible.
|
|
|
@ -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.
|
|
|
@ -1,2 +0,0 @@
|
||||||
- [Pro] Added the ability to create custom films.
|
|
||||||
- [Pro] Added the ability to select equipment profiles in use.
|
|
|
@ -1 +0,0 @@
|
||||||
- Performance improvements and bug fixes.
|
|
|
@ -1 +0,0 @@
|
||||||
- Cleaned up camera preview implementation.
|
|