Compare commits
8 commits
aa96544c25
...
3b79c19c06
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3b79c19c06 | ||
![]() |
91f90ae8e8 | ||
![]() |
79f702f7ea | ||
![]() |
f0110e0edf | ||
![]() |
0e45d98060 | ||
![]() |
cf471afdfd | ||
![]() |
d95b811d6c | ||
![]() |
2dee63e78e |
53
README.md
|
@ -23,11 +23,11 @@ Without further delay behold my new Lightmeter app inspired by Material You (a.k
|
||||||
# Screenshots
|
# Screenshots
|
||||||
|
|
||||||
<p float="center">
|
<p float="center">
|
||||||
<img src="screenshots/generated/android/android/light_metering-reflected.png" width="18.8%" />
|
<img src="screenshots/generated/ios/iphone65inch/light_metering-reflected.png" width="18.8%" />
|
||||||
<img src="screenshots/generated/android/android/light_timer.png" width="18.8%" />
|
<img src="screenshots/generated/ios/iphone65inch/light_equipment-profiles.png" width="18.8%" />
|
||||||
<img src="screenshots/generated/android/android/light_settings.png" width="18.8%" />
|
<img src="screenshots/generated/ios/iphone65inch/light_settings.png" width="18.8%" />
|
||||||
<img src="screenshots/generated/android/android/light_equipment-profiles.png" width="18.8%" />
|
<img src="screenshots/generated/ios/iphone65inch/light_timer.png" width="18.8%" />
|
||||||
<img src="screenshots/generated/android/android/dark_metering-reflected.png" width="18.8%" />
|
<img src="screenshots/generated/ios/iphone65inch/dark_metering-reflected.png" width="18.8%" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
# Development
|
# Development
|
||||||
|
@ -38,41 +38,18 @@ To build this app you need to install Flutter 3.24.5 stable. [How to install](ht
|
||||||
|
|
||||||
### 2. Project setup
|
### 2. Project setup
|
||||||
|
|
||||||
#### Restore _constants.dart_ file
|
#### Restore git-ignored files:
|
||||||
|
|
||||||
Create a file _lib/constants.dart_ and paste the following content:
|
For macOS you can just run the following script:
|
||||||
|
|
||||||
```dart
|
|
||||||
const String contactEmail = '';
|
|
||||||
const String iapServerUrl = '';
|
|
||||||
const String issuesReportUrl = '';
|
|
||||||
const String sourceCodeUrl = '';
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Stub IAP package
|
|
||||||
|
|
||||||
As part of the app's functionallity is in the private repo, you have to replace these lines in _pubspec.yaml_:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
m3_lightmeter_iap:
|
|
||||||
git:
|
|
||||||
url: "https://github.com/vodemn/m3_lightmeter_iap"
|
|
||||||
ref: main
|
|
||||||
```
|
|
||||||
|
|
||||||
with these:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
m3_lightmeter_iap:
|
|
||||||
path: iap
|
|
||||||
```
|
|
||||||
|
|
||||||
You can do it simply by running the script:
|
|
||||||
|
|
||||||
```console
|
```console
|
||||||
sh .github/scripts/stub_iap.sh
|
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.
|
> 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:
|
Then you can fetch all the neccessary dependencies and generate translation files by running the following commands:
|
||||||
|
@ -82,11 +59,7 @@ flutter pub get
|
||||||
flutter pub run intl_utils:generate
|
flutter pub run intl_utils:generate
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. (Optional) Install Firebase
|
### 3. Build
|
||||||
|
|
||||||
Out of the box Firebase Crashlytics won't work. If you want to add Crashlytics to your local build please follow [this guide](https://firebase.google.com/docs/flutter/setup).
|
|
||||||
|
|
||||||
### 4. Build
|
|
||||||
|
|
||||||
- Checkout [Build .apk](.github/workflows/build_apk.yml) workflow for Android
|
- Checkout [Build .apk](.github/workflows/build_apk.yml) workflow for Android
|
||||||
- Checkout [Build .ipa](.github/workflows/build_ipa.yml) workflow for iOS
|
- Checkout [Build .ipa](.github/workflows/build_ipa.yml) workflow for iOS
|
||||||
|
|
3
assets/release_notes/release_notes_en_1.0.4.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
- Fixed histogram being affected by spot metering.
|
||||||
|
- Improved text fields focus handling.
|
||||||
|
- Added German translation.
|
|
@ -109,17 +109,17 @@ void testE2E(String description) {
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Add ND to shoot another scene
|
/// Add ND to shoot another scene
|
||||||
await tester.openPickerAndSelect<NdValuePicker, NdValue>('2');
|
// await tester.openPickerAndSelect<NdValuePicker, NdValue>('2');
|
||||||
await _expectMeteringStateAndMeasure(
|
// await _expectMeteringStateAndMeasure(
|
||||||
tester,
|
// tester,
|
||||||
equipmentProfile: mockEquipmentProfiles[0],
|
// equipmentProfile: mockEquipmentProfiles[0],
|
||||||
film: mockFilms[0],
|
// film: mockFilms[0],
|
||||||
fastest: 'f/1.8 - 1/200',
|
// fastest: 'f/1.8 - 1/200',
|
||||||
slowest: 'f/16 - 1/2.5',
|
// slowest: 'f/16 - 1/2.5',
|
||||||
iso: '400',
|
// iso: '400',
|
||||||
nd: '2',
|
// nd: '2',
|
||||||
ev: mockPhotoEv100 + 2 - 1,
|
// ev: mockPhotoEv100 + 2 - 1,
|
||||||
);
|
// );
|
||||||
|
|
||||||
/// Select another lens without ND
|
/// Select another lens without ND
|
||||||
await tester.openPickerAndSelect<EquipmentProfilePicker, EquipmentProfile>(mockEquipmentProfiles[1].name);
|
await tester.openPickerAndSelect<EquipmentProfilePicker, EquipmentProfile>(mockEquipmentProfiles[1].name);
|
||||||
|
|
|
@ -53,6 +53,7 @@ class _MockIAPProvidersState extends State<MockIAPProviders> {
|
||||||
() => mockEquipmentProfilesStorageService.updateProfile(
|
() => mockEquipmentProfilesStorageService.updateProfile(
|
||||||
id: any<String>(named: 'id'),
|
id: any<String>(named: 'id'),
|
||||||
name: any<String>(named: 'name'),
|
name: any<String>(named: 'name'),
|
||||||
|
isUsed: any<bool>(named: 'isUsed'),
|
||||||
),
|
),
|
||||||
).thenAnswer((_) async {});
|
).thenAnswer((_) async {});
|
||||||
when(() => mockEquipmentProfilesStorageService.deleteProfile(any<String>())).thenAnswer((_) async {});
|
when(() => mockEquipmentProfilesStorageService.deleteProfile(any<String>())).thenAnswer((_) async {});
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
enum SupportedLocale { en, fr, ru, zh }
|
enum SupportedLocale { de, en, fr, ru, zh }
|
||||||
|
|
||||||
extension SupportedLocaleExtension on SupportedLocale {
|
extension SupportedLocaleExtension on SupportedLocale {
|
||||||
String get intlName => toString().replaceAll("SupportedLocale.", "");
|
String get intlName => toString().replaceAll("SupportedLocale.", "");
|
||||||
|
|
||||||
String get localizedName {
|
String get localizedName {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
|
case SupportedLocale.de:
|
||||||
|
return 'Deutsch';
|
||||||
case SupportedLocale.en:
|
case SupportedLocale.en:
|
||||||
return 'English';
|
return 'English';
|
||||||
case SupportedLocale.fr:
|
case SupportedLocale.fr:
|
||||||
|
|
165
lib/l10n/intl_de.arb
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
{
|
||||||
|
"@@locale": "de",
|
||||||
|
"fastestExposurePair": "Schnellstes",
|
||||||
|
"slowestExposurePair": "Langsamstes",
|
||||||
|
"ev": "EV",
|
||||||
|
"evValue": "{value} EV",
|
||||||
|
"@evValue": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iso": "ISO",
|
||||||
|
"filmSpeed": "Film Empfindlichkeit",
|
||||||
|
"nd": "ND",
|
||||||
|
"ndFilterFactor": "Neutraldichtefilter-Faktor",
|
||||||
|
"noExposurePairs": "Es gibt keine Belichtungspaare für die ausgewählten Einstellungen.",
|
||||||
|
"noCamerasDetected": "Scheinbar sind keine Kameras an das Gerät angeschlossen.",
|
||||||
|
"noCameraPermission": "Kamera-Erlaubnis nicht erteilt.",
|
||||||
|
"otherCameraError": "Beim Verbinden der Kamera ist ein Fehler aufgetreten.",
|
||||||
|
"none": "Keiner",
|
||||||
|
"cancel": "Abbrechen",
|
||||||
|
"select": "Auswählen",
|
||||||
|
"save": "Speichern",
|
||||||
|
"settings": "Einstellungen",
|
||||||
|
"metering": "Messung",
|
||||||
|
"fractionalStops": "Zwischenstufen",
|
||||||
|
"showFractionalStops": "Zwischenstufen anzeigen",
|
||||||
|
"halfStops": "1/2",
|
||||||
|
"thirdStops": "1/3",
|
||||||
|
"calibration": "Kalibration",
|
||||||
|
"calibrationMessage": "Die Genauigkeit der Messungen sind vollständig von der Hardware des Geräts abhängig. Deshalb ist es empfehlenswert diese App zu testen und EV-Kalibrationswerte einzustellen, die korrekte Messungen produzieren.",
|
||||||
|
"calibrationMessageCameraOnly": "Die Genauigkeit der Messungen sind vollständig von der Kamera des Geräts abhängig. Deshalb ist es empfehlenswert diese App zu testen und EV-Kalibrationswerte einzustellen, die korrekte Messungen produzieren.",
|
||||||
|
"camera": "Kamera",
|
||||||
|
"lightSensor": "Lichtsensor",
|
||||||
|
"showEv100": "EV\u2081\u2080\u2080 anzeigen",
|
||||||
|
"meteringScreenLayout": "Messansicht Layout",
|
||||||
|
"meteringScreenLayoutHint": "Verstecke Elemente von der Messansicht, damit sie nicht den Platz für Belichtungspaare verschwenden.",
|
||||||
|
"meteringScreenLayoutHintEquipmentProfiles": "Ausrüstungsprofil Auswahl",
|
||||||
|
"meteringScreenFeatureExtremeExposurePairs": "Schnellste & langsamste Belichtungspaare",
|
||||||
|
"meteringScreenFeatureFilmPicker": "Film Auswahl",
|
||||||
|
"cameraFeatures": "Kamerafunktionen",
|
||||||
|
"cameraFeatureSpotMetering": "Punkt-Messung",
|
||||||
|
"cameraFeatureSpotMeteringHint": "Halte die Kameraansicht gedrückt um den Messpunkt zu entfernen",
|
||||||
|
"cameraFeatureHistogram": "Histogramm",
|
||||||
|
"cameraFeatureHistogramHint": "Verwendung des Histogramms kann den Batterieverbrauch erhöhen",
|
||||||
|
"film": "Film",
|
||||||
|
"filmPush": "Film (push)",
|
||||||
|
"filmPull": "Film (pull)",
|
||||||
|
"filmReciprocityHint": "Korrigiert Belichtungszeiten länger als 1 Sekunde",
|
||||||
|
"equipmentProfileName": "Ausrüstungsprofilname",
|
||||||
|
"equipmentProfileNameHint": "Praktica MTL5B",
|
||||||
|
"equipmentProfileAllValues": "Alle",
|
||||||
|
"apertureValues": "Blend-Werte",
|
||||||
|
"apertureValuesFilterDescription": "Wähle die anzuzeigenden Blend-Werte aus. Die Werte sind normalerweise von dem verwendeten Objektiv bestimmt.",
|
||||||
|
"ndFilters": "ND Filter",
|
||||||
|
"ndFiltersFilterDescription": "Wähle die anzuzeigenden ND Filter aus. (Beispielsweise die Meistverwendeten)",
|
||||||
|
"shutterSpeedValues": "Belichtungszeiten",
|
||||||
|
"shutterSpeedValuesFilterDescription": "Wähle die anzuzeigenden Belichtungszeiten aus. Die Werte sind normalerweise von der Kamera bestimmt.",
|
||||||
|
"shutterSpeedManualShort": "B",
|
||||||
|
"shutterSpeedManual": "Manuell",
|
||||||
|
"isoValues": "ISO Werte",
|
||||||
|
"isoValuesFilterDescription": "Wähle die anzuzeigenden ISO Werte aus. (Beispielsweise die Meistverwendeten)",
|
||||||
|
"lensZoom": "Objektiv-Zoom",
|
||||||
|
"lensZoomDescription": "Wähle den Zoom, relativ zur Handykamera, dass mit dem Sucher der Kamera übereinstimmt.",
|
||||||
|
"equipmentProfile": "Ausrüstungsprofil",
|
||||||
|
"equipmentProfiles": "Ausrüstungsprofile",
|
||||||
|
"tapToAdd": "Tippe zum Hinzufügen",
|
||||||
|
"general": "Allgemein",
|
||||||
|
"keepScreenOn": "Bildschirm anbehalten",
|
||||||
|
"haptics": "Haptik",
|
||||||
|
"autostartTimer": "Timer auto-starten",
|
||||||
|
"volumeKeysAction": "Auslösen durch Lautstärketasten",
|
||||||
|
"language": "Sprache",
|
||||||
|
"chooseLanguage": "Sprache auswählen",
|
||||||
|
"theme": "Theme",
|
||||||
|
"chooseTheme": "Theme auswählen",
|
||||||
|
"themeLight": "Hell",
|
||||||
|
"themeDark": "Dunkel",
|
||||||
|
"themeSystemDefault": "Systemeinstellung",
|
||||||
|
"dynamicColor": "Dynamische Farbe",
|
||||||
|
"primaryColor": "Akzentfarbe",
|
||||||
|
"choosePrimaryColor": "Akzentfarbe auswählen",
|
||||||
|
"about": "Info",
|
||||||
|
"restorePurchases": "Käufe wiederherstellen",
|
||||||
|
"sourceCode": "Source code",
|
||||||
|
"reportIssue": "Problem melden",
|
||||||
|
"writeEmail": "Email schreiben",
|
||||||
|
"youDontHaveMailApp": "Es ist keine Email App installiert.",
|
||||||
|
"copyEmail": "Email kopieren",
|
||||||
|
"version": "Version",
|
||||||
|
"versionNumber": "{version} ({buildNumber})",
|
||||||
|
"@versionNumber": {
|
||||||
|
"placeholders": {
|
||||||
|
"version": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"buildNumber": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"proFeaturesTitle": "Lightmeter Pro",
|
||||||
|
"getPro": "Pro kaufen",
|
||||||
|
"featuresFree": "Gratis",
|
||||||
|
"featuresPro": "Pro",
|
||||||
|
"proFeaturesPromoText": "Lightmeter Pro liefert alles, was Sie brauchen, um die besten Aufnahmen zu machen!",
|
||||||
|
"proFeaturesWhatsIncluded": "Was ist enthalten?",
|
||||||
|
"featureReflectedLightMetering": "Messung von reflektiertem Licht",
|
||||||
|
"featureIncidentLightMetering": "Messung von einfallendem Licht",
|
||||||
|
"featureIsoAndNdValues": "Große Auswahl von ISO und ND Filtern",
|
||||||
|
"featureTheme": "Theme Anpassung",
|
||||||
|
"featureSpotMetering": "Punktmessung",
|
||||||
|
"featureHistogram": "Histogramm",
|
||||||
|
"featureListOfFilms": "Liste von 20+ Filmen mit Reziprozitätsformeln",
|
||||||
|
"featureCustomFilms": "Eigene Filme erstellen",
|
||||||
|
"featureEquipmentProfiles": "Ausrüstungsprofile",
|
||||||
|
"featureTimer": "Eingebauter Timer für Langzeitbelichtungen",
|
||||||
|
"featureMeteringScreenLayout": "Anpassbare Messansicht",
|
||||||
|
"proFeaturesSupportText": "Durch den Kauf von Lightmeter Pro unterstützen Sie den Entwickler und ermöglichen das Hinzufügen weiterer Funktionen.",
|
||||||
|
"getNowFor": "Jetzt für {price} kaufen",
|
||||||
|
"@getNowFor": {
|
||||||
|
"price": {
|
||||||
|
"version": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tooltipAdd": "Hinzufügen",
|
||||||
|
"tooltipClose": "Schließen",
|
||||||
|
"tooltipExpand": "Erweitern",
|
||||||
|
"tooltipCollapse": "Schrumpfen",
|
||||||
|
"tooltipCopy": "Kopieren",
|
||||||
|
"tooltipDelete": "Löschen",
|
||||||
|
"tooltipSelectAll": "Alle auswählen",
|
||||||
|
"tooltipDesecelectAll": "Keine auswählen",
|
||||||
|
"tooltipResetToZero": "Auf null zurücksetzen",
|
||||||
|
"tooltipUseLightSensor": "Lichtsensor verwenden",
|
||||||
|
"tooltipUseCamera": "Kamera verwenden",
|
||||||
|
"tooltipOpenSettings": "Einstellungen öffnen",
|
||||||
|
"exposurePair": "Belichtungspaar",
|
||||||
|
"whatsnew": "Was ist neu?",
|
||||||
|
"changesInVersion": "Änderungen in Version {version}:",
|
||||||
|
"@changesInVersion": {
|
||||||
|
"placeholders": {
|
||||||
|
"version": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"close": "Schließen",
|
||||||
|
"films": "Filme",
|
||||||
|
"filmsInUse": "Verwendete Filme",
|
||||||
|
"filmsCustom": "Eigene Filme",
|
||||||
|
"addFilmTitle": "Film hinzufügen",
|
||||||
|
"editFilmTitle": "Film bearbeiten",
|
||||||
|
"filmFormula": "Formel",
|
||||||
|
"filmFormulaExponential": "T=t^Rf",
|
||||||
|
"filmFormulaExponentialRf": "Rf",
|
||||||
|
"filmFormulaExponentialRfPlaceholder": "1.3",
|
||||||
|
"name": "Name",
|
||||||
|
"addEquipmentProfileTitle": "Ausrüstung hinzufügen",
|
||||||
|
"editEquipmentProfileTitle": "Ausrüstung bearbeiten"
|
||||||
|
}
|
|
@ -137,8 +137,8 @@ class _NameFieldBuilder extends StatelessWidget {
|
||||||
bottom: Dimens.paddingS / 2,
|
bottom: Dimens.paddingS / 2,
|
||||||
),
|
),
|
||||||
child: LightmeterTextField(
|
child: LightmeterTextField(
|
||||||
initialValue: state.name,
|
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
|
initialValue: state.name,
|
||||||
maxLength: 48,
|
maxLength: 48,
|
||||||
hintText: S.of(context).name,
|
hintText: S.of(context).name,
|
||||||
style: Theme.of(context).listTileTheme.titleTextStyle,
|
style: Theme.of(context).listTileTheme.titleTextStyle,
|
||||||
|
|
|
@ -132,7 +132,7 @@ class _ProFeaturesOverlay extends StatelessWidget {
|
||||||
);
|
);
|
||||||
final bool hasSpotMetering = UserPreferencesProvider.cameraFeatureOf(
|
final bool hasSpotMetering = UserPreferencesProvider.cameraFeatureOf(
|
||||||
context,
|
context,
|
||||||
CameraFeature.histogram,
|
CameraFeature.spotMetering,
|
||||||
);
|
);
|
||||||
return Stack(
|
return Stack(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
|
|
|
@ -1,33 +1,74 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
class LightmeterTextField extends TextFormField {
|
class LightmeterTextField extends StatefulWidget {
|
||||||
LightmeterTextField({
|
const LightmeterTextField({
|
||||||
super.controller,
|
this.autofocus = false,
|
||||||
super.autofocus,
|
this.controller,
|
||||||
super.initialValue,
|
this.hintText,
|
||||||
super.inputFormatters,
|
this.initialValue,
|
||||||
super.maxLength,
|
this.inputFormatters,
|
||||||
super.onChanged,
|
this.leading,
|
||||||
super.style,
|
this.maxLength,
|
||||||
super.textAlign,
|
this.onChanged,
|
||||||
Widget? leading,
|
this.style,
|
||||||
String? hintText,
|
this.textAlign = TextAlign.start,
|
||||||
}) : super(
|
});
|
||||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
|
||||||
maxLines: 1,
|
final bool autofocus;
|
||||||
decoration: InputDecoration(
|
final TextEditingController? controller;
|
||||||
counter: const SizedBox(),
|
final String? hintText;
|
||||||
contentPadding: EdgeInsets.zero,
|
final String? initialValue;
|
||||||
errorStyle: const TextStyle(fontSize: 0),
|
final List<TextInputFormatter>? inputFormatters;
|
||||||
icon: leading,
|
final Widget? leading;
|
||||||
hintText: hintText,
|
final int? maxLength;
|
||||||
),
|
final void Function(String)? onChanged;
|
||||||
validator: (value) {
|
final TextStyle? style;
|
||||||
if (value == null || value.isEmpty) {
|
final TextAlign textAlign;
|
||||||
return '';
|
|
||||||
} else {
|
@override
|
||||||
return null;
|
State<LightmeterTextField> createState() => _LightmeterTextFieldState();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
);
|
class _LightmeterTextFieldState extends State<LightmeterTextField> {
|
||||||
|
late final focusNode = FocusNode(debugLabel: widget.hintText);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextFormField(
|
||||||
|
autofocus: widget.autofocus,
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
controller: widget.controller,
|
||||||
|
focusNode: focusNode,
|
||||||
|
initialValue: widget.initialValue,
|
||||||
|
inputFormatters: widget.inputFormatters,
|
||||||
|
maxLength: widget.maxLength,
|
||||||
|
onChanged: widget.onChanged,
|
||||||
|
style: widget.style,
|
||||||
|
textAlign: widget.textAlign,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
counter: const SizedBox(),
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
errorStyle: const TextStyle(fontSize: 0),
|
||||||
|
icon: widget.leading,
|
||||||
|
hintText: widget.hintText,
|
||||||
|
),
|
||||||
|
onTapOutside: (event) {
|
||||||
|
focusNode.unfocus();
|
||||||
|
},
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return '';
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
focusNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -829,11 +829,11 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: main
|
ref: "v2.1.3"
|
||||||
resolved-ref: "1070317079c42afa7d3f600747470c403408071f"
|
resolved-ref: "4575586ff6114b780ca651b7ac7c272a4a4801eb"
|
||||||
url: "https://github.com/vodemn/m3_lightmeter_iap"
|
url: "https://github.com/vodemn/m3_lightmeter_iap"
|
||||||
source: git
|
source: git
|
||||||
version: "2.1.2+29"
|
version: "2.1.3+30"
|
||||||
m3_lightmeter_resources:
|
m3_lightmeter_resources:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -1562,4 +1562,4 @@ packages:
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.5.0 <4.0.0"
|
dart: ">=3.5.0 <4.0.0"
|
||||||
flutter: ">=3.24.0"
|
flutter: ">=3.24.5"
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
name: lightmeter
|
name: lightmeter
|
||||||
description: Lightmeter app inspired by Material 3 design system.
|
description: Lightmeter app inspired by Material 3 design system.
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
version: 1.0.3+58
|
version: 1.0.4+59
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.0.0 <4.0.0"
|
sdk: ">=3.0.0 <4.0.0"
|
||||||
|
flutter: "3.24.5"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
app_settings: 5.1.1
|
app_settings: 5.1.1
|
||||||
|
|
|
@ -30,11 +30,6 @@
|
||||||
"title": "Create multiple profiles",
|
"title": "Create multiple profiles",
|
||||||
"subtitle": "to match your\ncamera & lens setups"
|
"subtitle": "to match your\ncamera & lens setups"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"screenshotName": "light_equipment-profiles-iso-picker",
|
|
||||||
"title": "Fine-tune results",
|
|
||||||
"subtitle": "by selecting the values\nthat you use the most"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"screenshotName": "dark_metering-reflected",
|
"screenshotName": "dark_metering-reflected",
|
||||||
"title": "Match your style",
|
"title": "Match your style",
|
||||||
|
|
|
@ -15,7 +15,6 @@ import 'package:lightmeter/data/shared_prefs_service.dart';
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'package:lightmeter/res/theme.dart';
|
import 'package:lightmeter/res/theme.dart';
|
||||||
import 'package:lightmeter/screens/equipment_profiles/screen_equipment_profiles.dart';
|
|
||||||
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart';
|
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/iso_picker/widget_picker_iso.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/iso_picker/widget_picker_iso.dart';
|
||||||
import 'package:lightmeter/screens/metering/screen_metering.dart';
|
import 'package:lightmeter/screens/metering/screen_metering.dart';
|
||||||
|
@ -118,13 +117,9 @@ void main() {
|
||||||
|
|
||||||
await tester.tapDescendantTextOf<SettingsScreen>(S.current.equipmentProfiles);
|
await tester.tapDescendantTextOf<SettingsScreen>(S.current.equipmentProfiles);
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
await tester.tapDescendantTextOf<EquipmentProfilesScreen>(mockEquipmentProfiles.first.name);
|
await tester.tap(find.byIcon(Icons.edit_outlined).first);
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
await tester.takeScreenshotLight(binding, 'equipment-profiles');
|
await tester.takeScreenshotLight(binding, 'equipment-profiles');
|
||||||
|
|
||||||
await tester.tap(find.byIcon(Icons.iso_outlined).first);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await tester.takeScreenshotLight(binding, 'equipment-profiles-iso-picker');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/// and the additionally the first one with the dark theme
|
/// and the additionally the first one with the dark theme
|
||||||
|
@ -168,7 +163,7 @@ void main() {
|
||||||
ev: 5,
|
ev: 5,
|
||||||
exposurePair: timerExposurePair,
|
exposurePair: timerExposurePair,
|
||||||
);
|
);
|
||||||
await tester.tap(find.text(timerExposurePair.shutterSpeed.toString()));
|
await tester.tap(find.text(_mockFilm.reciprocityFailure(timerExposurePair.shutterSpeed).toString()));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
await tester.mockTimerResumedState(timerExposurePair.shutterSpeed);
|
await tester.mockTimerResumedState(timerExposurePair.shutterSpeed);
|
||||||
await tester.takeScreenshotLight(binding, 'timer');
|
await tester.takeScreenshotLight(binding, 'timer');
|
||||||
|
|
Before Width: | Height: | Size: 492 KiB After Width: | Height: | Size: 469 KiB |
Before Width: | Height: | Size: 221 KiB |
Before Width: | Height: | Size: 221 KiB After Width: | Height: | Size: 215 KiB |
Before Width: | Height: | Size: 235 KiB After Width: | Height: | Size: 235 KiB |
Before Width: | Height: | Size: 220 KiB After Width: | Height: | Size: 223 KiB |
Before Width: | Height: | Size: 500 KiB After Width: | Height: | Size: 478 KiB |
Before Width: | Height: | Size: 235 KiB After Width: | Height: | Size: 234 KiB |
Before Width: | Height: | Size: 219 KiB After Width: | Height: | Size: 220 KiB |
Before Width: | Height: | Size: 346 KiB After Width: | Height: | Size: 348 KiB |
Before Width: | Height: | Size: 159 KiB |
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 159 KiB |
Before Width: | Height: | Size: 177 KiB After Width: | Height: | Size: 181 KiB |
Before Width: | Height: | Size: 345 KiB After Width: | Height: | Size: 347 KiB |
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 169 KiB |
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 148 KiB |
Before Width: | Height: | Size: 507 KiB After Width: | Height: | Size: 511 KiB |
Before Width: | Height: | Size: 223 KiB |
Before Width: | Height: | Size: 227 KiB After Width: | Height: | Size: 224 KiB |
Before Width: | Height: | Size: 248 KiB After Width: | Height: | Size: 254 KiB |
Before Width: | Height: | Size: 501 KiB After Width: | Height: | Size: 505 KiB |
Before Width: | Height: | Size: 235 KiB After Width: | Height: | Size: 238 KiB |
Before Width: | Height: | Size: 206 KiB After Width: | Height: | Size: 207 KiB |
|
@ -1,4 +0,0 @@
|
||||||
dart run screenshots/convert_to_store_screenshots.dart -p android -d pixel_6 -l android
|
|
||||||
dart run screenshots/convert_to_store_screenshots.dart -p ios -d iphone_13_pro -l iphone55inch
|
|
||||||
dart run screenshots/convert_to_store_screenshots.dart -p ios -d iphone_13_pro -l iphone65inch
|
|
||||||
#dart run screenshots/convert_to_store_screenshots.dart -p ios -d ipad_pro_12.9-inch_6th_generation -l ipad13inch
|
|
|
@ -1 +1,3 @@
|
||||||
sh screenshots/scripts/generate_screenshots.sh "Pixel 6"
|
sh screenshots/scripts/generate_screenshots.sh "Pixel 6"
|
||||||
|
|
||||||
|
dart run screenshots/convert_to_store_screenshots.dart -p android -d pixel_6 -l android
|
|
@ -6,3 +6,6 @@ for i in "${simulators_array[@]}"; do # https://www.baeldung.com/linux/shell-scr
|
||||||
sh screenshots/scripts/generate_screenshots.sh "$i"
|
sh screenshots/scripts/generate_screenshots.sh "$i"
|
||||||
done
|
done
|
||||||
killall 'Simulator'
|
killall 'Simulator'
|
||||||
|
|
||||||
|
dart run screenshots/convert_to_store_screenshots.dart -p ios -d iphone_13_pro -l iphone55inch
|
||||||
|
dart run screenshots/convert_to_store_screenshots.dart -p ios -d iphone_13_pro -l iphone65inch
|
4
scripts/mocks/mock_constants.dart
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
const String contactEmail = '';
|
||||||
|
const String iapServerUrl = '';
|
||||||
|
const String issuesReportUrl = 'https://github.com/vodemn/m3_lightmeter/issues/new/choose';
|
||||||
|
const String sourceCodeUrl = 'https://github.com/vodemn/m3_lightmeter/';
|
30
scripts/mocks/mock_firebase.json
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"flutter": {
|
||||||
|
"platforms": {
|
||||||
|
"android": {
|
||||||
|
"default": {
|
||||||
|
"projectId": "mockproject-1234",
|
||||||
|
"appId": "1:123456789000:android:f1bf012572b04063",
|
||||||
|
"fileOutput": "android/app/google-services.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ios": {
|
||||||
|
"default": {
|
||||||
|
"projectId": "mockproject-1234",
|
||||||
|
"appId": "1:123456789000:ios:f1bf012572b04063",
|
||||||
|
"uploadDebugSymbols": true,
|
||||||
|
"fileOutput": "ios/Runner/GoogleService-Info.plist"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dart": {
|
||||||
|
"lib/firebase_options.dart": {
|
||||||
|
"projectId": "mockproject-1234",
|
||||||
|
"configurations": {
|
||||||
|
"android": "1:123456789000:android:f1bf012572b04063",
|
||||||
|
"ios": "1:123456789000:ios:f1bf012572b04063"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
scripts/mocks/mock_firebase_options.dart
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
||||||
|
|
||||||
|
class DefaultFirebaseOptions {
|
||||||
|
static FirebaseOptions get currentPlatform =>
|
||||||
|
FirebaseOptions(apiKey: '', appId: '', messagingSenderId: '', projectId: '');
|
||||||
|
}
|
12
scripts/setup_fork.sh
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
cp "scripts/mocks/mock_constants.dart" "lib/constants.dart"
|
||||||
|
cp "scripts/mocks/mock_firebase_options.dart" "lib/firebase_options.dart"
|
||||||
|
cp "scripts/mocks/mock_firebase.json" "firebase.json"
|
||||||
|
|
||||||
|
curl -H 'Accept: application/vnd.github.v3.raw' \
|
||||||
|
-o "android/app/google-services.json" \
|
||||||
|
-L "https://api.github.com/repos/firebase/quickstart-android/contents/mock-google-services.json"
|
||||||
|
curl -H 'Accept: application/vnd.github.v3.raw' \
|
||||||
|
-o "ios/Runner/GoogleService-Info.plist" \
|
||||||
|
-L "https://api.github.com/repos/firebase/quickstart-ios/contents/mock-GoogleService-Info.plist"
|
||||||
|
|
||||||
|
sh .github/scripts/stub_iap.sh
|
|
@ -7,6 +7,7 @@ void main() {
|
||||||
expect(SupportedLocale.fr.intlName, 'fr');
|
expect(SupportedLocale.fr.intlName, 'fr');
|
||||||
expect(SupportedLocale.ru.intlName, 'ru');
|
expect(SupportedLocale.ru.intlName, 'ru');
|
||||||
expect(SupportedLocale.zh.intlName, 'zh');
|
expect(SupportedLocale.zh.intlName, 'zh');
|
||||||
|
expect(SupportedLocale.de.intlName, 'de');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('localizedName', () {
|
test('localizedName', () {
|
||||||
|
@ -14,5 +15,6 @@ void main() {
|
||||||
expect(SupportedLocale.fr.localizedName, 'Français');
|
expect(SupportedLocale.fr.localizedName, 'Français');
|
||||||
expect(SupportedLocale.ru.localizedName, 'Русский');
|
expect(SupportedLocale.ru.localizedName, 'Русский');
|
||||||
expect(SupportedLocale.zh.localizedName, '简体中文');
|
expect(SupportedLocale.zh.localizedName, '简体中文');
|
||||||
|
expect(SupportedLocale.de.localizedName, 'Deutsch');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|