Compare commits

..

12 commits

Author SHA1 Message Date
Vadim
2a7dad2c38 migrated to artifacts v4 2025-01-23 18:33:30 +01:00
Vadim
2ee4d3ef53 Merge branch 'cd' of https://github.com/vodemn/m3_lightmeter into cd 2025-01-23 18:32:55 +01:00
Vadim
9a2ef35eed moved firebase options to gitignore 2025-01-23 18:32:12 +01:00
Vadim
cd752aaf89 restore firebase options in workflows 2025-01-23 18:32:12 +01:00
Vadim
b614af9a97 moved firebase options to gitignore 2025-01-23 18:14:53 +01:00
Vadim
85b84000ae restore firebase options in workflows 2025-01-23 18:14:02 +01:00
Vadim
43989e5e5f Merge branch 'cd' of https://github.com/vodemn/m3_lightmeter into cd 2025-01-23 18:10:22 +01:00
Vadim
8ecab836a3 ML-209 Camera preview orientation is wrong (#210)
* lock orientation to `portraitUp`

* removed custom rotator for camera preview
2025-01-23 18:05:39 +01:00
Vadim
2439f7bfff Fixed builds being pushed to main (#211) 2025-01-23 18:05:39 +01:00
Vadim
bccd68f1ff fixed builds being pushed to main 2025-01-23 17:52:46 +01:00
github-actions[bot]
5e8f66d75c Release v1.0.1 2025-01-21 19:04:54 +00:00
Vadim
75dc9aaf13
Added builds to CI (#208)
* Update README.md

* Set exact Flutter version for workflows

* Added stub `DefaultFirebaseOptions`

* Fixed `rm`

* Removed `rm`

* Update .gitignore

* Added readable name to ci workflow

* Build -> Development

* Update ci.yml

* Extract merged native libraries

* More descriptive run name

* Delete no longer used artifacts

* Replaced "Build ..." flow with "Create release"

* renamed other flows

* try using script for iap stub

* typo

* typo

* typo

* removed working dir

* added comment to stub_iap.sh

* checkout first

* increment build number by script

* Update increment_build_number.sh

* fixed iap repo

* stub

* updated stub script to work with tags

* depend on step conclusion

* check PR number

* run integration tests before build

* reuse Build Android workflow

* added stage backend option

* reuse Build iOS workflow

* temporeraly skip release jobs

* [ios] use distribution profile for release builds

* temporary skip tests

* typo

* checkout actions

* incremented macos runner version

* Restore GoogleService-Info.plist

* Restore firebase_app_id_file.json

* style

* separated android and ios builds

* fixed invalid workflow

* simplified release workflow tree

* fixed android keystore path

* enabled integration tests

* added option to skip integration tests

* fixed android folders...

* enabled releases

* increment build number for ios

* upload ipa to app store

* test ipa upload

* typo

* try to force ipa upload

* removed flavor from ipa artefact name

* try manual ipa upload

* switched to ubuntu for upload

* decode to repo

* Update create_release.yml

* auth with username + password

* reverted temporary settings

* typo

* disable pre-release integration tests by default

* fixed integration tests

* increased integration tests timeout

* delete ipa after upload

* delete all artifacts after the run

* fixed integration tests

* reduce integration tests timeout

* build apk with latest macos runner

* allow to skip release to one of the stores

* added build checks to PR checks

* fixed inputs naming

* increased jvm heap
2025-01-21 19:43:14 +01:00
12 changed files with 72 additions and 164 deletions

View file

@ -115,7 +115,7 @@ jobs:
- name: Download release notes
continue-on-error: true
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: ${{ env.RELEASE_NOTES_ARTIFACT_NAME }}
path: ${{ env.RELEASE_NOTES_PATH }}
@ -136,7 +136,7 @@ jobs:
run: flutter build ${{ inputs.binary-type }} $BUILD_ARGS
- name: Upload ${{ inputs.binary-type }} to artifacts
uses: actions/upload-artifact@v3
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

@ -108,7 +108,7 @@ jobs:
- name: Download release notes
continue-on-error: true
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: ${{ env.RELEASE_NOTES_ARTIFACT_NAME }}
path: ${{ env.RELEASE_NOTES_PATH }}
@ -135,7 +135,7 @@ jobs:
--export-options-plist=ios/Runner/ExportOptions.plist
- name: Upload artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: m3_lightmeter_ipa
path: build/ios/ipa/lightmeter.ipa

View file

@ -69,7 +69,7 @@ jobs:
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@v3
uses: actions/upload-artifact@v4
with:
name: ${{ env.RELEASE_NOTES_ARTIFACT_NAME }}
path: ${{ env.RELEASE_NOTES_FILE }}
@ -112,24 +112,24 @@ jobs:
submodules: recursive
- name: Download apk
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: m3_lightmeter_apk
- name: Increment build number & replace version number
run: bash ./.github/scripts/increment_build_number.sh ${{ github.event.inputs.version }}
- name: Download release notes
uses: actions/download-artifact@v3
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 -A
git add --all -- ":!app-prod-release.apk"
git commit -m "Release v${{ inputs.version }}"
- name: Push to main
@ -161,7 +161,7 @@ jobs:
submodules: recursive
- name: Download app bundle
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: m3_lightmeter_appbundle
@ -171,7 +171,7 @@ jobs:
(cd base/lib && zip -r "$OLDPWD/merged_native_libs.zip" .)
- name: Download release notes
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: ${{ env.RELEASE_NOTES_ARTIFACT_NAME }}
@ -213,7 +213,7 @@ jobs:
submodules: recursive
- name: Download ipa
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: m3_lightmeter_ipa

View file

@ -36,8 +36,10 @@ jobs:
if: steps.fetch-iap.conclusion != 'success'
run: bash ./.github/scripts/stub_iap.sh
- name: Restore constants.dart
run: bash .github/scripts/restore_from_base64.sh "${{ secrets.CONSTANTS }}" "lib/constants.dart"
- name: Restore secrets
run: |
bash .github/scripts/restore_from_base64.sh "${{ secrets.CONSTANTS }}" "lib/constants.dart"
bash .github/scripts/restore_from_base64.sh "${{ secrets.FIREBASE_OPTIONS }}" "lib/firebase_options.dart"
- uses: subosito/flutter-action@v2
with:

View file

@ -27,8 +27,9 @@ jobs:
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"

4
.gitignore vendored
View file

@ -58,8 +58,8 @@ 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
lib/firebase_options.dart
lib/constants.dart
coverage/
test/coverage_helper_test.dart

View file

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

View file

@ -1,67 +0,0 @@
// File generated by FlutterFire CLI.
// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb, TargetPlatform;
/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for web - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for macos - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.windows:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for windows - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions android = FirebaseOptions(
apiKey: '',
messagingSenderId: '',
projectId: '',
storageBucket: '',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: '',
appId: '',
messagingSenderId: '',
projectId: '',
storageBucket: '',
iosClientId: '',
iosBundleId: '',
);
}

View file

@ -132,6 +132,7 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
await _cameraController!.initialize();
await _cameraController!.setFlashMode(FlashMode.off);
await _cameraController!.lockCaptureOrientation(DeviceOrientation.portraitUp);
_zoomRange = await Future.wait<double>([
_cameraController!.getMinZoomLevel(),

View file

@ -1,50 +0,0 @@
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class CameraView extends StatelessWidget {
final CameraController controller;
const CameraView({required this.controller, super.key});
@override
Widget build(BuildContext context) {
final value = controller.value;
return ValueListenableBuilder<CameraValue>(
valueListenable: controller,
builder: (_, __, Widget? child) => AspectRatio(
aspectRatio: _isLandscape(value) ? value.aspectRatio : (1 / value.aspectRatio),
child: Stack(
children: [
RotatedBox(
quarterTurns: _getQuarterTurns(value),
child: controller.buildPreview(),
),
child ?? const SizedBox(),
],
),
),
);
}
bool _isLandscape(CameraValue value) {
return <DeviceOrientation>[DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]
.contains(_getApplicableOrientation(value));
}
int _getQuarterTurns(CameraValue value) {
final Map<DeviceOrientation, int> turns = <DeviceOrientation, int>{
DeviceOrientation.portraitUp: 0,
DeviceOrientation.landscapeRight: 1,
DeviceOrientation.portraitDown: 2,
DeviceOrientation.landscapeLeft: 3,
};
return turns[_getApplicableOrientation(value)]!;
}
DeviceOrientation _getApplicableOrientation(CameraValue value) {
return value.isRecordingVideo
? value.recordingOrientation!
: (value.previewPauseOrientation ?? value.lockedCaptureOrientation ?? value.deviceOrientation);
}
}

View file

@ -1,18 +1,17 @@
import 'package:camera/camera.dart';
import 'package:camera/camera.dart' as camera;
import 'package:flutter/material.dart';
import 'package:lightmeter/data/models/camera_feature.dart';
import 'package:lightmeter/platform_config.dart';
import 'package:lightmeter/providers/user_preferences_provider.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/screens/metering/components/camera_container/components/camera_preview/components/camera_spot_detector/widget_camera_spot_detector.dart';
import 'package:lightmeter/screens/metering/components/camera_container/components/camera_preview/components/camera_view/widget_camera_view.dart';
import 'package:lightmeter/screens/metering/components/camera_container/components/camera_preview/components/camera_view_placeholder/widget_placeholder_camera_view.dart';
import 'package:lightmeter/screens/metering/components/camera_container/components/camera_preview/components/histogram/widget_histogram.dart';
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
import 'package:lightmeter/utils/context_utils.dart';
class CameraPreview extends StatefulWidget {
final CameraController? controller;
final camera.CameraController? controller;
final CameraErrorType? error;
final ValueChanged<Offset?> onSpotTap;
@ -53,7 +52,7 @@ class _CameraPreviewState extends State<CameraPreview> {
}
class _CameraPreviewBuilder extends StatefulWidget {
final CameraController controller;
final camera.CameraController controller;
final ValueChanged<Offset?> onSpotTap;
const _CameraPreviewBuilder({
@ -97,29 +96,15 @@ class _CameraPreviewBuilderState extends State<_CameraPreviewBuilder> {
}
return ValueListenableBuilder<bool>(
valueListenable: _initializedNotifier,
builder: (context, value, child) => value
? Stack(
alignment: Alignment.bottomCenter,
children: [
CameraView(controller: widget.controller),
if (context.isPro) ...[
if (UserPreferencesProvider.cameraFeatureOf(
context,
CameraFeature.histogram,
))
Positioned(
left: Dimens.grid8,
right: Dimens.grid8,
bottom: Dimens.grid16,
child: CameraHistogram(controller: widget.controller),
),
if (UserPreferencesProvider.cameraFeatureOf(
context,
CameraFeature.spotMetering,
))
CameraSpotDetector(onSpotTap: widget.onSpotTap),
],
],
builder: (context, value, _) => value
? camera.CameraPreview(
widget.controller,
child: context.isPro
? _ProFeaturesOverlay(
controller: widget.controller,
onSpotTap: widget.onSpotTap,
)
: const SizedBox.shrink(),
)
: const SizedBox.shrink(),
);
@ -129,3 +114,38 @@ class _CameraPreviewBuilderState extends State<_CameraPreviewBuilder> {
_initializedNotifier.value = widget.controller.value.isInitialized;
}
}
class _ProFeaturesOverlay extends StatelessWidget {
final camera.CameraController controller;
final ValueChanged<Offset?> onSpotTap;
const _ProFeaturesOverlay({
required this.controller,
required this.onSpotTap,
});
@override
Widget build(BuildContext context) {
final bool hasHistogram = UserPreferencesProvider.cameraFeatureOf(
context,
CameraFeature.histogram,
);
final bool hasSpotMetering = UserPreferencesProvider.cameraFeatureOf(
context,
CameraFeature.histogram,
);
return Stack(
alignment: Alignment.bottomCenter,
children: [
if (hasHistogram)
Positioned(
left: Dimens.grid8,
right: Dimens.grid8,
bottom: Dimens.grid16,
child: CameraHistogram(controller: controller),
),
if (hasSpotMetering) CameraSpotDetector(onSpotTap: onSpotTap),
],
);
}
}

View file

@ -1,7 +1,7 @@
name: lightmeter
description: Lightmeter app inspired by Material 3 design system.
publish_to: "none"
version: 1.0.0+55
version: 1.0.1+56
environment:
sdk: ">=3.0.0 <4.0.0"