added some camera changes and included IOS support

This commit is contained in:
2025-10-18 19:02:26 +02:00
parent 2e3a69a23e
commit be9b13fb15
7 changed files with 766 additions and 80 deletions
+56
View File
@@ -0,0 +1,56 @@
# Uncomment this line to define a global platform for your project
platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
# Ensure minimum iOS deployment target
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
config.build_settings['ENABLE_BITCODE'] = 'NO'
# Fix for Swift compatibility
if config.build_settings['WRAPPER_EXTENSION'] == 'bundle'
config.build_settings['DEVELOPMENT_TEAM'] = 'YourTeamID'
end
end
end
end
+22
View File
@@ -45,5 +45,27 @@
<true/> <true/>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to capture and analyze shooting targets.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photo library access to save and load target images.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app needs permission to save captured target images to your photo library.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access for video recording.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>This app needs local network access to connect to camera streams.</string>
<key>NSBonjourServices</key>
<array>
<string>_http._tcp</string>
<string>_https._tcp</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
</dict> </dict>
</plist> </plist>
-18
View File
@@ -318,24 +318,6 @@ class _CalendarScreenState extends State<CalendarScreen> {
color: isToday ? accentColor : Colors.black87, color: isToday ? accentColor : Colors.black87,
), ),
), ),
if (isToday) ...[
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: accentColor,
borderRadius: BorderRadius.circular(8),
),
child: Text(
_t('NOW'),
style: const TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
],
], ],
), ),
), ),
+233 -13
View File
@@ -24,6 +24,10 @@ class _CameraScreenState extends State<CameraScreen> {
int _numberOfBullets = 5; int _numberOfBullets = 5;
String _selectedLanguage = 'English'; String _selectedLanguage = 'English';
// Brightness and contrast controls
double _brightness = 0.0; // -1.0 to 1.0
double _contrast = 1.0; // 0.0 to 2.0
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -31,9 +35,24 @@ class _CameraScreenState extends State<CameraScreen> {
_initCamera(); _initCamera();
_loadBulletColor(); // 👈 _loadBulletColor(); // 👈
_loadLanguage(); _loadLanguage();
_loadBrightnessContrast();
YOLODetector.create().then((detector) => _yoloDetector = detector); YOLODetector.create().then((detector) => _yoloDetector = detector);
} }
Future<void> _loadBrightnessContrast() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
_brightness = prefs.getDouble('brightness') ?? 0.0;
_contrast = prefs.getDouble('contrast') ?? 1.0;
});
}
Future<void> _saveBrightnessContrast() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setDouble('brightness', _brightness);
await prefs.setDouble('contrast', _contrast);
}
String _t(String key) { String _t(String key) {
return AppTranslations.translate(key, _selectedLanguage); return AppTranslations.translate(key, _selectedLanguage);
} }
@@ -240,6 +259,175 @@ Color _getColorFromName(String colorName) {
} }
void _showHelpDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
backgroundColor: Colors.grey[900],
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_t('Help'),
style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
_buildHelpItem(Icons.camera_alt, _t('Tap camera button to capture and detect bullets')),
const SizedBox(height: 12),
_buildHelpItem(Icons.filter_9_plus, _t('Bottom left number: Set expected bullet count (1-5)')),
const SizedBox(height: 12),
_buildHelpItem(Icons.brightness_6, _t('Sun icon: Adjust brightness and contrast for better visibility')),
const SizedBox(height: 12),
_buildHelpItem(Icons.crop_square, _t('Align target inside the square frame')),
const SizedBox(height: 20),
Center(
child: ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
style: ElevatedButton.styleFrom(
backgroundColor: _bulletColor,
foregroundColor: Colors.white,
),
child: Text(_t('Got it!')),
),
),
],
),
),
);
},
);
}
Widget _buildHelpItem(IconData icon, String text) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icon, color: _bulletColor, size: 20),
const SizedBox(width: 12),
Expanded(
child: Text(
text,
style: const TextStyle(color: Colors.white, fontSize: 14),
),
),
],
);
}
void _showBrightnessContrastDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setDialogState) {
return Dialog(
backgroundColor: Colors.grey[900],
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
_t('Brightness & Contrast'),
style: const TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 24),
// Brightness slider
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${_t('Brightness')}: ${(_brightness * 100).toInt()}',
style: const TextStyle(color: Colors.white, fontSize: 16),
),
Slider(
value: _brightness,
min: -1.0,
max: 1.0,
divisions: 40,
activeColor: _bulletColor,
onChanged: (value) {
setState(() {
_brightness = value;
});
setDialogState(() {});
_saveBrightnessContrast();
},
),
],
),
const SizedBox(height: 16),
// Contrast slider
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${_t('Contrast')}: ${(_contrast * 100).toInt()}%',
style: const TextStyle(color: Colors.white, fontSize: 16),
),
Slider(
value: _contrast,
min: 0.5,
max: 2.0,
divisions: 30,
activeColor: _bulletColor,
onChanged: (value) {
setState(() {
_contrast = value;
});
setDialogState(() {});
_saveBrightnessContrast();
},
),
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {
setState(() {
_brightness = 0.0;
_contrast = 1.0;
});
setDialogState(() {});
_saveBrightnessContrast();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[800],
foregroundColor: Colors.white,
),
child: Text(_t('Reset')),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
style: ElevatedButton.styleFrom(
backgroundColor: _bulletColor,
foregroundColor: Colors.white,
),
child: Text(_t('Done')),
),
],
),
],
),
),
);
},
);
},
);
}
// Update the bullet count and save it // Update the bullet count and save it
void _selectBulletCount(int number) { void _selectBulletCount(int number) {
setState(() { setState(() {
@@ -274,26 +462,58 @@ Color _getColorFromName(String colorName) {
), ),
centerTitle: true, centerTitle: true,
actions: [ actions: [
// Display the selected bullet count
IconButton( IconButton(
icon: Text( icon: const Icon(Icons.help_outline, color: Colors.white),
'$_numberOfBullets', onPressed: _showHelpDialog,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
onPressed: _showBulletDialog, // Show dialog on tap
), ),
const SizedBox(width: 16), // Space after the number const SizedBox(width: 16),
], ],
), ),
bottomNavigationBar: BottomAppBar( bottomNavigationBar: BottomAppBar(
color: Colors.grey[900], color: Colors.grey[900],
shape: const CircularNotchedRectangle(), shape: const CircularNotchedRectangle(),
notchMargin: 8.0, notchMargin: 8.0,
child: SizedBox(height: 60), child: SizedBox(
height: 60,
child: Row(
children: [
// Left - Bullet count (just number)
Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 16.0),
child: InkWell(
onTap: _showBulletDialog,
child: Text(
'$_numberOfBullets',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
),
),
// Center space for FAB
const SizedBox(width: 80),
// Right section - Brightness
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
icon: const Icon(Icons.brightness_6, color: Colors.white, size: 24),
onPressed: _showBrightnessContrastDialog,
),
],
),
),
],
),
),
), ),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
@@ -327,7 +547,7 @@ Color _getColorFromName(String colorName) {
height: screenSize.width * 0.9, height: screenSize.width * 0.9,
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( border: Border.all(
color: _bulletColor.withOpacity(0.9), color: _bulletColor.withValues(alpha: 0.9),
width: 3, width: 3,
), ),
), ),
+310 -49
View File
@@ -34,6 +34,10 @@ class _CameraStreamScreenState extends State<CameraStreamScreen> with SingleTick
late YOLODetector _yoloDetector; late YOLODetector _yoloDetector;
int _numberOfBullets = 5; int _numberOfBullets = 5;
// Brightness and contrast controls
double _brightness = 0.0; // -1.0 to 1.0
double _contrast = 1.0; // 0.0 to 2.0
late AnimationController _zoomAnimController; late AnimationController _zoomAnimController;
Animation<Matrix4>? _zoomAnimation; Animation<Matrix4>? _zoomAnimation;
Matrix4? _originalMatrix; Matrix4? _originalMatrix;
@@ -53,6 +57,7 @@ class _CameraStreamScreenState extends State<CameraStreamScreen> with SingleTick
_loadBulletCount(); _loadBulletCount();
_loadSelectedFilter(); _loadSelectedFilter();
_loadBulletColor(); // 👈 add this to actually load the saved bullet color _loadBulletColor(); // 👈 add this to actually load the saved bullet color
_loadBrightnessContrast();
YOLODetector.create().then((detector) => _yoloDetector = detector); YOLODetector.create().then((detector) => _yoloDetector = detector);
@@ -68,6 +73,20 @@ class _CameraStreamScreenState extends State<CameraStreamScreen> with SingleTick
); );
} }
Future<void> _loadBrightnessContrast() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
_brightness = prefs.getDouble('brightness') ?? 0.0;
_contrast = prefs.getDouble('contrast') ?? 1.0;
});
}
Future<void> _saveBrightnessContrast() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setDouble('brightness', _brightness);
await prefs.setDouble('contrast', _contrast);
}
Future<void> _loadLanguage() async { Future<void> _loadLanguage() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
setState(() { setState(() {
@@ -428,6 +447,177 @@ Color _getColorFromName(String colorName) {
await prefs.setInt('bulletCount', number); await prefs.setInt('bulletCount', number);
} }
void _showHelpDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
backgroundColor: Colors.grey[900],
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_t('Help'),
style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
_buildHelpItem(Icons.camera_alt, _t('Tap camera button to capture and detect bullets')),
const SizedBox(height: 12),
_buildHelpItem(Icons.filter_9_plus, _t('Bottom left number: Set expected bullet count (1-5)')),
const SizedBox(height: 12),
_buildHelpItem(Icons.brightness_6, _t('Sun icon: Adjust brightness and contrast for better visibility')),
const SizedBox(height: 12),
_buildHelpItem(Icons.filter_alt, _t('Filter icon: Apply color filters (None, Greyscale, Invert)')),
const SizedBox(height: 12),
_buildHelpItem(Icons.pinch, _t('Pinch to zoom on the camera stream')),
const SizedBox(height: 20),
Center(
child: ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
style: ElevatedButton.styleFrom(
backgroundColor: _bulletColor,
foregroundColor: Colors.white,
),
child: Text(_t('Got it!')),
),
),
],
),
),
);
},
);
}
Widget _buildHelpItem(IconData icon, String text) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icon, color: _bulletColor, size: 20),
const SizedBox(width: 12),
Expanded(
child: Text(
text,
style: const TextStyle(color: Colors.white, fontSize: 14),
),
),
],
);
}
void _showBrightnessContrastDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setDialogState) {
return Dialog(
backgroundColor: Colors.grey[900],
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
_t('Brightness & Contrast'),
style: const TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 24),
// Brightness slider
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${_t('Brightness')}: ${(_brightness * 100).toInt()}',
style: const TextStyle(color: Colors.white, fontSize: 16),
),
Slider(
value: _brightness,
min: -1.0,
max: 1.0,
divisions: 40,
activeColor: _bulletColor,
onChanged: (value) {
setState(() {
_brightness = value;
});
setDialogState(() {});
_saveBrightnessContrast();
},
),
],
),
const SizedBox(height: 16),
// Contrast slider
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${_t('Contrast')}: ${(_contrast * 100).toInt()}%',
style: const TextStyle(color: Colors.white, fontSize: 16),
),
Slider(
value: _contrast,
min: 0.5,
max: 2.0,
divisions: 30,
activeColor: _bulletColor,
onChanged: (value) {
setState(() {
_contrast = value;
});
setDialogState(() {});
_saveBrightnessContrast();
},
),
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {
setState(() {
_brightness = 0.0;
_contrast = 1.0;
});
setDialogState(() {});
_saveBrightnessContrast();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[800],
foregroundColor: Colors.white,
),
child: Text(_t('Reset')),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
style: ElevatedButton.styleFrom(
backgroundColor: _bulletColor,
foregroundColor: Colors.white,
),
child: Text(_t('Done')),
),
],
),
],
),
),
);
},
);
},
);
}
@override @override
void dispose() { void dispose() {
_zoomAnimController.dispose(); _zoomAnimController.dispose();
@@ -449,24 +639,70 @@ void dispose() {
} }
ColorFilter _buildColorFilter() { ColorFilter _buildColorFilter() {
// Base filter matrix
List<double> baseMatrix;
switch (_selectedFilter) { switch (_selectedFilter) {
case ImageFilterType.greyscale: case ImageFilterType.greyscale:
return const ColorFilter.matrix([ baseMatrix = [
0.2126, 0.7152, 0.0722, 0, 0, 0.2126, 0.7152, 0.0722, 0, 0,
0.2126, 0.7152, 0.0722, 0, 0, 0.2126, 0.7152, 0.0722, 0, 0,
0.2126, 0.7152, 0.0722, 0, 0, 0.2126, 0.7152, 0.0722, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 1, 0,
]); ];
break;
case ImageFilterType.invert: case ImageFilterType.invert:
return const ColorFilter.matrix([ baseMatrix = [
-1, 0, 0, 0, 255, -1.0, 0.0, 0.0, 0.0, 255.0,
0, -1, 0, 0, 255, 0.0, -1.0, 0.0, 0.0, 255.0,
0, 0, -1, 0, 255, 0.0, 0.0, -1.0, 0.0, 255.0,
0, 0, 0, 1, 0, 0.0, 0.0, 0.0, 1.0, 0.0,
]); ];
break;
default: default:
return const ColorFilter.mode(Colors.transparent, BlendMode.dst); baseMatrix = [
1.0, 0.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0,
];
} }
// Apply brightness and contrast adjustments
final b = _brightness * 255.0; // Convert to 0-255 range
final c = _contrast;
// Combine filter with brightness/contrast
final adjustedMatrix = <double>[
c, 0.0, 0.0, 0.0, b,
0.0, c, 0.0, 0.0, b,
0.0, 0.0, c, 0.0, b,
0.0, 0.0, 0.0, 1.0, 0.0,
];
// Multiply base matrix with adjustment matrix
return ColorFilter.matrix(_multiplyMatrices(baseMatrix, adjustedMatrix));
}
List<double> _multiplyMatrices(List<double> a, List<double> b) {
// For simplicity, just apply brightness/contrast on top of base filter
final result = <double>[];
for (int i = 0; i < a.length; i++) {
result.add(a[i]);
}
// Apply contrast to RGB components
result[0] = result[0] * b[0]; // R
result[6] = result[6] * b[6]; // G
result[12] = result[12] * b[12]; // B
// Apply brightness
result[4] = result[4] + b[4]; // R offset
result[9] = result[9] + b[9]; // G offset
result[14] = result[14] + b[14]; // B offset
return result;
} }
@override @override
@@ -482,19 +718,8 @@ void dispose() {
), ),
actions: [ actions: [
IconButton( IconButton(
icon: Text( icon: const Icon(Icons.help_outline, color: Colors.white),
'$_numberOfBullets', onPressed: _showHelpDialog,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
onPressed: _showBulletDialog,
),
IconButton(
icon: const Icon(Icons.filter_alt, color: Colors.white),
onPressed: _showFilterDialog,
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
], ],
@@ -507,31 +732,33 @@ void dispose() {
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
return Center( return Center(
child: InteractiveViewer( child: (_isError || _streamUrl == null)
clipBehavior: Clip.hardEdge, ? _buildErrorContent()
constrained: false, : InteractiveViewer(
boundaryMargin: EdgeInsets.zero, clipBehavior: Clip.hardEdge,
minScale: 1.0, constrained: false,
maxScale: 5.0, boundaryMargin: EdgeInsets.zero,
transformationController: _transformationController, minScale: 1.0,
child: SizedBox( maxScale: 5.0,
width: constraints.maxWidth, transformationController: _transformationController,
height: constraints.maxHeight, child: SizedBox(
child: (_isError || _streamUrl == null) width: constraints.maxWidth,
? _buildErrorContent() height: constraints.maxHeight,
: RepaintBoundary( child: RepaintBoundary(
child: ColorFiltered( child: ColorFiltered(
colorFilter: _buildColorFilter(), colorFilter: _buildColorFilter(),
child: Mjpeg( child: Mjpeg(
stream: _streamUrl!, stream: _streamUrl!,
isLive: true, isLive: true,
fit: BoxFit.contain, fit: BoxFit.contain,
error: (context, error, stackTrace) => _buildErrorContent(), error: (context, error, stackTrace) {
return _buildErrorContent();
},
),
),
), ),
), ),
), ),
),
),
); );
}, },
), ),
@@ -540,7 +767,7 @@ void dispose() {
if (_isCapturing) if (_isCapturing)
Positioned.fill( Positioned.fill(
child: Container( child: Container(
color: Colors.black.withOpacity(0.4), color: Colors.black.withValues(alpha: 0.4),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
@@ -570,11 +797,45 @@ void dispose() {
notchMargin: 8.0, notchMargin: 8.0,
child: SizedBox( child: SizedBox(
height: 60, height: 60,
child: Stack( child: Row(
alignment: Alignment.center,
children: [ children: [
Align( // Left - Bullet count (just number)
alignment: Alignment.center, Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 16.0),
child: InkWell(
onTap: _showBulletDialog,
child: Text(
'$_numberOfBullets',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
),
),
// Center space for FAB
const SizedBox(width: 80),
// Right section - Brightness and Filter
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
icon: const Icon(Icons.brightness_6, color: Colors.white, size: 24),
onPressed: _showBrightnessContrastDialog,
),
IconButton(
icon: const Icon(Icons.filter_alt, color: Colors.white, size: 24),
onPressed: _showFilterDialog,
),
],
),
), ),
], ],
), ),
+1
View File
@@ -63,6 +63,7 @@ class _TargetScreenState extends State<TargetScreen> {
}); });
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
+144
View File
@@ -342,6 +342,94 @@ class AppTranslations {
'Slovenian': 'Ni mogoče naložiti kamere', 'Slovenian': 'Ni mogoče naložiti kamere',
}, },
// Mode Selection
'Select Detection Mode': {
'English': 'Select Detection Mode',
'Slovenian': 'Izberi način zaznavanja',
},
'Capture & Shoot': {
'English': 'Capture & Shoot',
'Slovenian': 'Zajemi in streljaj',
},
'Take single photo and detect bullets': {
'English': 'Take single photo and detect bullets',
'Slovenian': 'Zajemi fotografijo in zazna zadetke',
},
'Live Detection': {
'English': 'Live Detection',
'Slovenian': 'Živa zaznava',
},
'Continuous monitoring with real-time scoring': {
'English': 'Continuous monitoring with real-time scoring',
'Slovenian': 'Neprekinjeno spremljanje z ocenjevanjem v živo',
},
// Live Detection Screen
'Capture Baseline': {
'English': 'Capture Baseline',
'Slovenian': 'Zajemi osnovno sliko',
},
'Position target and capture empty baseline': {
'English': 'Position target and capture empty baseline',
'Slovenian': 'Postavi tarčo in zajemi prazno osnovno sliko',
},
'Start Detection': {
'English': 'Start Detection',
'Slovenian': 'Začni zaznavanje',
},
'Stop Detection': {
'English': 'Stop Detection',
'Slovenian': 'Ustavi zaznavanje',
},
'Bullets Detected': {
'English': 'Bullets Detected',
'Slovenian': 'Zaznani zadetki',
},
'Current Score': {
'English': 'Current Score',
'Slovenian': 'Trenutna ocena',
},
'Monitoring...': {
'English': 'Monitoring...',
'Slovenian': 'Spremljanje...',
},
'Save & Exit': {
'English': 'Save & Exit',
'Slovenian': 'Shrani in zapri',
},
'Capturing baseline...': {
'English': 'Capturing baseline...',
'Slovenian': 'Zajemanje osnove...',
},
'Baseline captured!': {
'English': 'Baseline captured!',
'Slovenian': 'Osnova zajeta!',
},
'Analyzing frame...': {
'English': 'Analyzing frame...',
'Slovenian': 'Analiza slike...',
},
'New bullet detected!': {
'English': 'New bullet detected!',
'Slovenian': 'Nov zadetek zaznan!',
},
'Calculate Score': {
'English': 'Calculate Score',
'Slovenian': 'Izračunaj rezultat',
},
'Target reached': {
'English': 'Target reached',
'Slovenian': 'Cilj dosežen',
},
'bullets': {
'English': 'bullets',
'Slovenian': 'zadetkov',
},
'Click Calculate Score to continue': {
'English': 'Click Calculate Score to continue',
'Slovenian': 'Kliknite Izračunaj rezultat za nadaljevanje',
},
// Delete confirmations // Delete confirmations
'Are you sure you want to delete this file?': { 'Are you sure you want to delete this file?': {
'English': 'Are you sure you want to delete this file?', 'English': 'Are you sure you want to delete this file?',
@@ -367,6 +455,62 @@ class AppTranslations {
'English': 'and all of its contents?', 'English': 'and all of its contents?',
'Slovenian': 'in vso njeno vsebino?', 'Slovenian': 'in vso njeno vsebino?',
}, },
// Help dialog translations
'Help': {
'English': 'Help',
'Slovenian': 'Pomoč',
},
'Tap camera button to capture and detect bullets': {
'English': 'Tap camera button to capture and detect bullets',
'Slovenian': 'Tapnite gumb kamere za zajem in zaznavanje zadetkov',
},
'Bottom left number: Set expected bullet count (1-5)': {
'English': 'Bottom left number: Set expected bullet count (1-5)',
'Slovenian': 'Spodaj levo številka: Nastavite število zadetkov (1-5)',
},
'Sun icon: Adjust brightness and contrast for better visibility': {
'English': 'Sun icon: Adjust brightness and contrast for better visibility',
'Slovenian': 'Ikona sonca: Prilagodite svetlost in kontrast za boljšo vidljivost',
},
'Filter icon: Apply color filters (None, Greyscale, Invert)': {
'English': 'Filter icon: Apply color filters (None, Greyscale, Invert)',
'Slovenian': 'Ikona filtra: Uporabite barvne filtre (Brez, Sivine, Inverzno)',
},
'Pinch to zoom on the camera stream': {
'English': 'Pinch to zoom on the camera stream',
'Slovenian': 'Povečajte s prstoma na video pretoku',
},
'Align target inside the square frame': {
'English': 'Align target inside the square frame',
'Slovenian': 'Poravnajte tarčo znotraj kvadratnega okvirja',
},
'Got it!': {
'English': 'Got it!',
'Slovenian': 'Razumem!',
},
// Brightness & Contrast dialog translations
'Brightness & Contrast': {
'English': 'Brightness & Contrast',
'Slovenian': 'Svetlost in kontrast',
},
'Brightness': {
'English': 'Brightness',
'Slovenian': 'Svetlost',
},
'Contrast': {
'English': 'Contrast',
'Slovenian': 'Kontrast',
},
'Reset': {
'English': 'Reset',
'Slovenian': 'Ponastavi',
},
'Done': {
'English': 'Done',
'Slovenian': 'Končano',
},
}; };
static String translate(String key, String language) { static String translate(String key, String language) {