diff --git a/assets/target.png b/assets/target.png index c2574b4..b341847 100644 Binary files a/assets/target.png and b/assets/target.png differ diff --git a/assets/target_clean.png b/assets/target_clean.png deleted file mode 100644 index b341847..0000000 Binary files a/assets/target_clean.png and /dev/null differ diff --git a/assets/targets/Target1.jpg b/assets/targets/Target1.jpg new file mode 100644 index 0000000..30318f9 Binary files /dev/null and b/assets/targets/Target1.jpg differ diff --git a/assets/targets/Target11.jpg b/assets/targets/Target11.jpg new file mode 100644 index 0000000..953cd7f Binary files /dev/null and b/assets/targets/Target11.jpg differ diff --git a/assets/targets/Target14.jpg b/assets/targets/Target14.jpg new file mode 100644 index 0000000..4249d0c Binary files /dev/null and b/assets/targets/Target14.jpg differ diff --git a/assets/targets/Target15.jpg b/assets/targets/Target15.jpg new file mode 100644 index 0000000..4b1b024 Binary files /dev/null and b/assets/targets/Target15.jpg differ diff --git a/assets/targets/Target2.jpg b/assets/targets/Target2.jpg new file mode 100644 index 0000000..2d85172 Binary files /dev/null and b/assets/targets/Target2.jpg differ diff --git a/assets/targets/Target3.jpg b/assets/targets/Target3.jpg new file mode 100644 index 0000000..0389def Binary files /dev/null and b/assets/targets/Target3.jpg differ diff --git a/assets/targets/Target4.jpg b/assets/targets/Target4.jpg new file mode 100644 index 0000000..48e2962 Binary files /dev/null and b/assets/targets/Target4.jpg differ diff --git a/assets/targets/Target5.jpg b/assets/targets/Target5.jpg new file mode 100644 index 0000000..9cfa545 Binary files /dev/null and b/assets/targets/Target5.jpg differ diff --git a/assets/targets/Target6.jpg b/assets/targets/Target6.jpg new file mode 100644 index 0000000..398c947 Binary files /dev/null and b/assets/targets/Target6.jpg differ diff --git a/assets/targets/Target7.jpg b/assets/targets/Target7.jpg new file mode 100644 index 0000000..ff3f7b4 Binary files /dev/null and b/assets/targets/Target7.jpg differ diff --git a/assets/targets/image (10).jpg b/assets/targets/image (10).jpg deleted file mode 100644 index be46bc4..0000000 Binary files a/assets/targets/image (10).jpg and /dev/null differ diff --git a/assets/targets/image (11).jpg b/assets/targets/image (11).jpg deleted file mode 100644 index d368831..0000000 Binary files a/assets/targets/image (11).jpg and /dev/null differ diff --git a/assets/targets/image (12).jpg b/assets/targets/image (12).jpg deleted file mode 100644 index 708c18e..0000000 Binary files a/assets/targets/image (12).jpg and /dev/null differ diff --git a/assets/targets/image (13).jpg b/assets/targets/image (13).jpg deleted file mode 100644 index b4b288c..0000000 Binary files a/assets/targets/image (13).jpg and /dev/null differ diff --git a/assets/targets/image (14).jpg b/assets/targets/image (14).jpg deleted file mode 100644 index ff89465..0000000 Binary files a/assets/targets/image (14).jpg and /dev/null differ diff --git a/assets/targets/image (16).jpg b/assets/targets/image (16).jpg deleted file mode 100644 index ab5d66c..0000000 Binary files a/assets/targets/image (16).jpg and /dev/null differ diff --git a/assets/targets/image (17).jpg b/assets/targets/image (17).jpg deleted file mode 100644 index fb5b208..0000000 Binary files a/assets/targets/image (17).jpg and /dev/null differ diff --git a/assets/targets/image (18).jpg b/assets/targets/image (18).jpg deleted file mode 100644 index 00be973..0000000 Binary files a/assets/targets/image (18).jpg and /dev/null differ diff --git a/assets/targets/image (19).jpg b/assets/targets/image (19).jpg deleted file mode 100644 index 8cb11d0..0000000 Binary files a/assets/targets/image (19).jpg and /dev/null differ diff --git a/assets/targets/image (20).jpg b/assets/targets/image (20).jpg deleted file mode 100644 index 39e48a9..0000000 Binary files a/assets/targets/image (20).jpg and /dev/null differ diff --git a/assets/targets/image (21).jpg b/assets/targets/image (21).jpg deleted file mode 100644 index 7d6a804..0000000 Binary files a/assets/targets/image (21).jpg and /dev/null differ diff --git a/assets/targets/image (23).jpg b/assets/targets/image (23).jpg deleted file mode 100644 index 2b9f8c3..0000000 Binary files a/assets/targets/image (23).jpg and /dev/null differ diff --git a/assets/targets/image (24).jpg b/assets/targets/image (24).jpg deleted file mode 100644 index aa693d4..0000000 Binary files a/assets/targets/image (24).jpg and /dev/null differ diff --git a/assets/targets/image (25).jpg b/assets/targets/image (25).jpg deleted file mode 100644 index 0484e26..0000000 Binary files a/assets/targets/image (25).jpg and /dev/null differ diff --git a/assets/targets/image (26).jpg b/assets/targets/image (26).jpg deleted file mode 100644 index 23657ac..0000000 Binary files a/assets/targets/image (26).jpg and /dev/null differ diff --git a/assets/targets/image (29).jpg b/assets/targets/image (29).jpg deleted file mode 100644 index 91ed36a..0000000 Binary files a/assets/targets/image (29).jpg and /dev/null differ diff --git a/assets/targets/image (5).jpg b/assets/targets/image (5).jpg deleted file mode 100644 index 9929a58..0000000 Binary files a/assets/targets/image (5).jpg and /dev/null differ diff --git a/assets/targets/image (6).jpg b/assets/targets/image (6).jpg deleted file mode 100644 index 57ddc6d..0000000 Binary files a/assets/targets/image (6).jpg and /dev/null differ diff --git a/assets/targets/image (7).jpg b/assets/targets/image (7).jpg deleted file mode 100644 index dda932e..0000000 Binary files a/assets/targets/image (7).jpg and /dev/null differ diff --git a/assets/targets/image (8).jpg b/assets/targets/image (8).jpg deleted file mode 100644 index 919f037..0000000 Binary files a/assets/targets/image (8).jpg and /dev/null differ diff --git a/assets/targets/image (9).jpg b/assets/targets/image (9).jpg deleted file mode 100644 index d1c265f..0000000 Binary files a/assets/targets/image (9).jpg and /dev/null differ diff --git a/lib/screens/preview_screen.dart b/lib/screens/preview_screen.dart index f771814..0d6079e 100644 --- a/lib/screens/preview_screen.dart +++ b/lib/screens/preview_screen.dart @@ -38,7 +38,7 @@ class _PreviewScreenState extends State { Uint8List? _cachedTargetImage; Uint8List? _cachedRealImage; - final String targetAssetPath = 'assets/target.png'; + late String targetAssetPath; final Offset fixedRingTargetCenter = const Offset(602.51, 600.43); final Size fixedRingTargetSize = const Size(355.94, 359.27); @@ -58,15 +58,23 @@ class _PreviewScreenState extends State { Color get _activeColorWithOpacity => _activeColor.withOpacity(_bulletOpacity); String _selectedLanguage = 'English'; + Color _backgroundColor = Colors.grey[900]!; @override void initState() { super.initState(); _realImageDetections = widget.detections; - _targetImageFuture = _loadAndDecodeTargetImage(); - _loadBulletColor(); // 👈 Add this - _loadLanguage(); - _runDetectionAndMapping(); + targetAssetPath = 'assets/target.png'; // Default + _initializeScreen(); + } + + Future _initializeScreen() async { + await Future.wait([ + _loadSelectedTarget(), + _loadBulletColor(), + _loadLanguage(), + ]); + await _runDetectionAndMapping(); } String _t(String key) { @@ -80,6 +88,71 @@ class _PreviewScreenState extends State { }); } + Future _loadSelectedTarget() async { + final prefs = await SharedPreferences.getInstance(); + // For detection mode (cameraWithDetections), always use default target + // For presentation mode (mappedTarget), use selected target + final selectedTarget = _mode == PreviewMode.mappedTarget + ? (prefs.getString('selectedTarget') ?? 'assets/target.png') + : 'assets/target.png'; + + // Update the target path and trigger image load + targetAssetPath = selectedTarget; + _targetImageFuture = _loadAndDecodeTargetImage(); + _backgroundColor = _getBackgroundColorForTarget(selectedTarget); + + // Await the image decoding to ensure it's populated before rebuilding + _targetImageDecoded = await _targetImageFuture; + if (_targetImageDecoded != null) { + _cachedTargetImage = Uint8List.fromList(img.encodePng(_targetImageDecoded!)); + } + + setState(() { + // Trigger rebuild with the new target + }); + } + + Color _getBackgroundColorForTarget(String targetPath) { + // Map each target to its background color (extracted from first pixel of each image) + switch (targetPath) { + case 'assets/target.png': + return const Color(0xFFFFFFFF); // White + + case 'assets/targets/Target1.jpg': + return const Color(0xFFE8ECD5); // Light beige + + case 'assets/targets/Target2.jpg': + return const Color(0xFFC1DF87); // Light green + + case 'assets/targets/Target3.jpg': + return const Color(0xFFFEFEFE); // Off-white + + case 'assets/targets/Target4.jpg': + return const Color(0xFFD42931); // Red + + case 'assets/targets/Target5.jpg': + return const Color(0xFF02050A); // Very dark blue/black + + case 'assets/targets/Target6.jpg': + return const Color(0xFF000000); // Black + + case 'assets/targets/Target7.jpg': + return const Color(0xFFFFD454); // Bright yellow + + case 'assets/targets/Target11.jpg': + return const Color(0xFF1E5E81); // Dark teal blue + + case 'assets/targets/Target14.jpg': + return const Color(0xFFFEFEFC); // Off-white + + case 'assets/targets/Target15.jpg': + return const Color(0xFF98926E); // Olive green + + default: + return Colors.grey[900]!; // Fallback to dark gray + } + } + Future _loadBulletColor() async { final prefs = await SharedPreferences.getInstance(); final selectedColor = prefs.getString('bulletColor') ?? 'Blue'; @@ -783,7 +856,7 @@ Color _getBulletButtonColor(int index) { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.white, + backgroundColor: _backgroundColor, appBar: AppBar( backgroundColor: Colors.grey[900], iconTheme: const IconThemeData(color: Colors.white), @@ -799,12 +872,13 @@ Widget build(BuildContext context) { : Icons.center_focus_strong, ), color: Colors.white, - onPressed: () { + onPressed: () async { setState(() { _mode = _mode == PreviewMode.mappedTarget ? PreviewMode.cameraWithDetections : PreviewMode.mappedTarget; }); + await _loadSelectedTarget(); // Reload target based on new mode }, tooltip: _mode == PreviewMode.mappedTarget ? _t("Show Camera Detections") diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index df82dea..b9791d1 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -47,6 +47,20 @@ class _SettingsScreenState extends State { {'code': 'sl', 'name': 'Slovenian', 'flag': '🇸🇮'}, ]; + String _selectedTarget = 'assets/target.png'; + final List _availableTargets = [ + 'assets/target.png', + 'assets/targets/Target1.jpg', + 'assets/targets/Target2.jpg', + 'assets/targets/Target3.jpg', + 'assets/targets/Target4.jpg', + 'assets/targets/Target5.jpg', + 'assets/targets/Target6.jpg', + 'assets/targets/Target7.jpg', + 'assets/targets/Target11.jpg', + 'assets/targets/Target14.jpg', + 'assets/targets/Target15.jpg', + ]; @override void initState() { @@ -54,6 +68,7 @@ class _SettingsScreenState extends State { _loadCameraIPs(); _loadBulletColor(); _loadLanguage(); + _loadSelectedTarget(); } @@ -134,6 +149,29 @@ Future _saveLanguage(String language) async { setState(() => _selectedLanguage = language); } +Future _loadSelectedTarget() async { + final prefs = await SharedPreferences.getInstance(); + String savedTarget = prefs.getString('selectedTarget') ?? 'assets/target.png'; + + setState(() { + _selectedTarget = savedTarget; + }); +} + +Future _saveSelectedTarget(String target) async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setString('selectedTarget', target); + setState(() => _selectedTarget = target); +} + +String _getTargetDisplayName(String targetPath) { + if (targetPath == 'assets/target.png') { + return 'Default Target'; + } + return targetPath + .replaceAll('assets/targets/', '') + .replaceAll('.jpg', ''); +} Future _loadCameraIPs() async { final prefs = await SharedPreferences.getInstance(); @@ -317,6 +355,8 @@ Future _saveLanguage(String language) async { const SizedBox(height: 16), _buildBulletColorSelector(), const SizedBox(height: 16), + _buildTargetSelector(), + const SizedBox(height: 16), _buildCameraList(), ], @@ -507,6 +547,87 @@ Future _saveLanguage(String language) async { ); } +Widget _buildTargetSelector() { + return Container( + margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 0), + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.grey.withValues(alpha: 0.3), + blurRadius: 8, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Header + Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: Colors.grey[900], + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(12), + topRight: Radius.circular(12), + ), + ), + child: Row( + children: [ + Container( + width: 4, + height: 16, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(2), + ), + ), + const SizedBox(width: 12), + Text( + _t("Select Target"), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ], + ), + ), + // Content + GestureDetector( + onTap: _showTargetPopup, + child: Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(12), + bottomRight: Radius.circular(12), + ), + ), + child: Row( + children: [ + Expanded( + child: Text( + _getTargetDisplayName(_selectedTarget), + style: const TextStyle(color: Colors.black87, fontSize: 14), + ), + ), + const Icon(Icons.chevron_right, color: Colors.black54), + ], + ), + ), + ), + ], + ), + ); +} + void _showLanguagePopup() { showDialog( context: context, @@ -646,6 +767,130 @@ void _showBulletColorPopup() { ); } +void _showTargetPopup() { + String previewTarget = _selectedTarget; + + showDialog( + context: context, + builder: (context) { + return StatefulBuilder( + builder: (context, innerSetState) { + + return Dialog( + backgroundColor: Colors.grey[900], + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + _t("Select Target"), + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const SizedBox(height: 20), + // Image Preview + Container( + width: 200, + height: 200, + decoration: BoxDecoration( + border: Border.all(color: Colors.white24), + borderRadius: BorderRadius.circular(8), + ), + child: Image.asset( + previewTarget, + fit: BoxFit.contain, + errorBuilder: (context, error, stackTrace) { + return Container( + color: Colors.grey[800], + child: const Center( + child: Icon(Icons.image_not_supported, color: Colors.white54), + ), + ); + }, + ), + ), + const SizedBox(height: 20), + // Target List + SizedBox( + width: double.maxFinite, + height: 300, + child: ListView( + children: _availableTargets.map((targetPath) { + final isSelected = targetPath == previewTarget; + final displayName = _getTargetDisplayName(targetPath); + + return Padding( + padding: const EdgeInsets.only(bottom: 12.0), + child: SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () { + innerSetState(() => previewTarget = targetPath); + }, + style: ElevatedButton.styleFrom( + backgroundColor: isSelected + ? _getColorFromName(_selectedBulletColor) + : Colors.grey[800], + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Text(displayName), + ), + ), + ); + }).toList(), + ), + ), + const SizedBox(height: 12), + // Action Buttons + Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () async { + await _saveSelectedTarget(previewTarget); + if (context.mounted) { + Navigator.of(context).pop(); + } + }, + style: ElevatedButton.styleFrom( + backgroundColor: _getColorFromName(_selectedBulletColor), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Text(_t("Apply")), + ), + ), + const SizedBox(width: 12), + Expanded( + child: TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text(_t("Cancel"), style: const TextStyle(color: Colors.white70)), + ), + ), + ], + ), + ], + ), + ), + ); + }, + ); + }, + ); +} + Widget _buildCameraList() { return Container( margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 0), diff --git a/lib/screens/target_view.dart b/lib/screens/target_view.dart index a35b283..f5e8db2 100644 --- a/lib/screens/target_view.dart +++ b/lib/screens/target_view.dart @@ -34,20 +34,28 @@ class _TargetViewScreenState extends State { int? _highlightedBulletIndex; int? _pressedScoreGroup; late String _targetFilePath; - - final String targetAssetPath = 'assets/target.png'; + late String targetAssetPath; final double bulletSize = 19; Color _bulletColor = Colors.blue; // default/fallback bullet color String _selectedLanguage = 'English'; + Color _backgroundColor = Colors.white; @override void initState() { super.initState(); - _loadBulletColor(); - _loadLanguage(); - _loadJsonAndImage(); + targetAssetPath = 'assets/target.png'; // Default + _initializeScreen(); + } + + Future _initializeScreen() async { + await Future.wait([ + _loadBulletColor(), + _loadLanguage(), + _loadSelectedTarget(), + ]); + await _loadJsonAndImage(); } String _t(String key) { @@ -61,6 +69,54 @@ class _TargetViewScreenState extends State { }); } + Future _loadSelectedTarget() async { + final prefs = await SharedPreferences.getInstance(); + final selectedTarget = prefs.getString('selectedTarget') ?? 'assets/target.png'; + targetAssetPath = selectedTarget; + _backgroundColor = _getBackgroundColorForTarget(selectedTarget); + } + + Color _getBackgroundColorForTarget(String targetPath) { + // Map each target to its background color (extracted from first pixel of each image) + switch (targetPath) { + case 'assets/target.png': + return const Color(0xFFFFFFFF); // White + + case 'assets/targets/Target1.jpg': + return const Color(0xFFE8ECD5); // Light beige + + case 'assets/targets/Target2.jpg': + return const Color(0xFFC1DF87); // Light green + + case 'assets/targets/Target3.jpg': + return const Color(0xFFFEFEFE); // Off-white + + case 'assets/targets/Target4.jpg': + return const Color(0xFFD42931); // Red + + case 'assets/targets/Target5.jpg': + return const Color(0xFF02050A); // Very dark blue/black + + case 'assets/targets/Target6.jpg': + return const Color(0xFF000000); // Black + + case 'assets/targets/Target7.jpg': + return const Color(0xFFFFD454); // Bright yellow + + case 'assets/targets/Target11.jpg': + return const Color(0xFF1E5E81); // Dark teal blue + + case 'assets/targets/Target14.jpg': + return const Color(0xFFFEFEFC); // Off-white + + case 'assets/targets/Target15.jpg': + return const Color(0xFF98926E); // Olive green + + default: + return Colors.white; // Fallback to white + } + } + Future _loadBulletColor() async { final prefs = await SharedPreferences.getInstance(); final selectedColor = prefs.getString('bulletColor') ?? 'Blue'; @@ -194,7 +250,7 @@ class _TargetViewScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.white, + backgroundColor: _backgroundColor, appBar: AppBar( title: Row( mainAxisSize: MainAxisSize.min, @@ -365,7 +421,7 @@ class _TargetViewScreenState extends State { ), const SizedBox(height: 12), Container( - color: Colors.white, + color: _backgroundColor, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), child: Row( children: widget.isCombinedView diff --git a/pubspec.yaml b/pubspec.yaml index e46e921..5f625e9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,5 +57,7 @@ flutter: - assets/logo.png - assets/logo.svg - assets/target.png + - assets/target.svg - assets/google_logo.png - assets/best_float32_nms.tflite + - assets/targets/