Skip to content

Commit 2dbac02

Browse files
author
Kame
committed
Fix icons not displayed in 2 grid and Fix Import of IRPLUS xml file using NEC protocol
1 parent de3b86e commit 2dbac02

19 files changed

Lines changed: 1151 additions & 226 deletions

android/local.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
sdk.dir=/usr/lib/android-sdk
22
flutter.sdk=/home/intra/snap/flutter/common/flutter
33
flutter.buildMode=release
4-
flutter.versionName=2.0.12
5-
flutter.versionCode=24
4+
flutter.versionName=2.0.13
5+
flutter.versionCode=25
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[NEW]
2+
Add more icons (459 icons)
3+
Unified button naming across app (text/image/icon + icon names).
4+
Fixed 2-grid icon rendering. Added command search in macro and quick-tile pickers.
5+
Added macro step visual previews.
6+
Added Undo snackbars for button/macro/device-control removals.
7+
Improved empty states with "What next" + CTA (incl. Quick Settings "Set Power tile").
8+
Added color contrast normalization/accessibility for custom button colors.
9+
Added Semantics/touch-target and large-text tweaks.
10+
11+
[BUG]
12+
Fix icons not displayed in 2 grid
13+
Fix Import of IRPLUS xml file using NEC protocol
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import 'package:flutter/material.dart';
2+
3+
const double _kMinContrastRatio = 4.5;
4+
5+
Color normalizeAccessibleButtonColor(Color color) {
6+
Color candidate = color.withAlpha(0xFF);
7+
Color fg = bestButtonForeground(candidate);
8+
if (_contrastRatio(candidate, fg) >= _kMinContrastRatio) return candidate;
9+
10+
final hsl = HSLColor.fromColor(candidate);
11+
final useLightText = fg == Colors.white;
12+
for (int i = 0; i < 12; i++) {
13+
final nextLightness = (hsl.lightness + (useLightText ? -0.04 : 0.04))
14+
.clamp(0.0, 1.0);
15+
candidate = hsl.withLightness(nextLightness).toColor().withAlpha(0xFF);
16+
fg = bestButtonForeground(candidate);
17+
if (_contrastRatio(candidate, fg) >= _kMinContrastRatio) return candidate;
18+
}
19+
20+
return candidate;
21+
}
22+
23+
Color bestButtonForeground(Color background) {
24+
final bg = background.withAlpha(0xFF);
25+
final whiteRatio = _contrastRatio(bg, Colors.white);
26+
final blackRatio = _contrastRatio(bg, Colors.black);
27+
return whiteRatio >= blackRatio ? Colors.white : Colors.black;
28+
}
29+
30+
Color resolveButtonBackground(Color? customColor, Color fallback) {
31+
if (customColor == null) return fallback;
32+
return normalizeAccessibleButtonColor(customColor);
33+
}
34+
35+
Color resolveButtonForeground(Color? customColor, Color fallback) {
36+
if (customColor == null) return fallback;
37+
return bestButtonForeground(normalizeAccessibleButtonColor(customColor));
38+
}
39+
40+
double _contrastRatio(Color a, Color b) {
41+
final la = a.computeLuminance();
42+
final lb = b.computeLuminance();
43+
final light = la > lb ? la : lb;
44+
final dark = la > lb ? lb : la;
45+
return (light + 0.05) / (dark + 0.05);
46+
}

lib/utils/button_label.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import 'package:irblaster_controller/utils/remote.dart';
2+
import 'package:irblaster_controller/widgets/icon_picker.dart';
3+
4+
String displayButtonLabel(
5+
IRButton button, {
6+
String fallback = 'Unnamed',
7+
String iconFallback = 'Icon',
8+
}) {
9+
final label = formatButtonDisplayName(button.image).trim();
10+
if (label.isNotEmpty) return label;
11+
12+
if (button.iconCodePoint != null) {
13+
final iconName = iconPickerNameFor(
14+
codePoint: button.iconCodePoint!,
15+
fontFamily: button.iconFontFamily,
16+
);
17+
if (iconName != null && iconName.trim().isNotEmpty) return iconName;
18+
return iconFallback;
19+
}
20+
21+
return fallback;
22+
}
23+
24+
String displayButtonRefLabel(
25+
String? raw, {
26+
String fallback = 'Unknown',
27+
}) {
28+
final pretty = formatButtonDisplayName((raw ?? '').trim());
29+
return pretty.isEmpty ? fallback : pretty;
30+
}

lib/utils/remote.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class IRButton {
1717
final Map<String, dynamic>? protocolParams;
1818
final int? iconCodePoint;
1919
final String? iconFontFamily;
20+
final String? iconFontPackage;
2021
final int? buttonColor;
2122

2223
const IRButton({
@@ -31,6 +32,7 @@ class IRButton {
3132
this.protocolParams,
3233
this.iconCodePoint,
3334
this.iconFontFamily,
35+
this.iconFontPackage,
3436
this.buttonColor,
3537
});
3638

@@ -46,6 +48,7 @@ class IRButton {
4648
'protocolParams': protocolParams,
4749
'iconCodePoint': iconCodePoint,
4850
'iconFontFamily': iconFontFamily,
51+
'iconFontPackage': iconFontPackage,
4952
'buttonColor': buttonColor,
5053
};
5154

@@ -64,6 +67,10 @@ class IRButton {
6467
protocolParams: (pp is Map) ? Map<String, dynamic>.from(pp) : null,
6568
iconCodePoint: json['iconCodePoint'] is int ? json['iconCodePoint'] as int? : int.tryParse('${json['iconCodePoint'] ?? ''}'),
6669
iconFontFamily: json['iconFontFamily'] as String?,
70+
iconFontPackage: _resolveIconFontPackage(
71+
json['iconFontPackage'] as String?,
72+
json['iconFontFamily'] as String?,
73+
),
6774
buttonColor: json['buttonColor'] is int ? json['buttonColor'] as int? : int.tryParse('${json['buttonColor'] ?? ''}'),
6875
);
6976
}
@@ -80,6 +87,7 @@ class IRButton {
8087
Map<String, dynamic>? protocolParams,
8188
int? iconCodePoint,
8289
String? iconFontFamily,
90+
String? iconFontPackage,
8391
int? buttonColor,
8492
}) {
8593
return IRButton(
@@ -94,11 +102,24 @@ class IRButton {
94102
protocolParams: protocolParams ?? this.protocolParams,
95103
iconCodePoint: iconCodePoint ?? this.iconCodePoint,
96104
iconFontFamily: iconFontFamily ?? this.iconFontFamily,
105+
iconFontPackage: iconFontPackage ?? this.iconFontPackage,
97106
buttonColor: buttonColor ?? this.buttonColor,
98107
);
99108
}
100109
}
101110

111+
String? _resolveIconFontPackage(String? explicitPackage, String? fontFamily) {
112+
final pkg = explicitPackage?.trim();
113+
if (pkg != null && pkg.isNotEmpty) return pkg;
114+
115+
final family = fontFamily?.trim();
116+
if (family == null || family.isEmpty) return null;
117+
if (family.toLowerCase().contains('fontawesome')) {
118+
return 'font_awesome_flutter';
119+
}
120+
return null;
121+
}
122+
102123
class Remote {
103124
int id;
104125
final List<IRButton> buttons;
@@ -177,6 +198,14 @@ Future<List<Remote>> readRemotes() async {
177198
bm['id'] = uuid.v4();
178199
mutated = true;
179200
}
201+
final ff = (bm['iconFontFamily'] as String?)?.trim();
202+
final fp = (bm['iconFontPackage'] as String?)?.trim();
203+
if ((fp == null || fp.isEmpty) &&
204+
ff != null &&
205+
ff.toLowerCase().contains('fontawesome')) {
206+
bm['iconFontPackage'] = 'font_awesome_flutter';
207+
mutated = true;
208+
}
180209
}
181210
}
182211
}

lib/utils/remotes_io.dart

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,15 +1301,10 @@ String _lircHexFromAddrCmdExplicit(int addr16, int cmd16) {
13011301
final int c1 = (cmd16 >> 8) & 0xFF;
13021302
final int c2 = cmd16 & 0xFF;
13031303

1304-
final int l1 = _bitReverse(a1);
1305-
final int l2 = _bitReverse(a2);
1306-
final int l3 = _bitReverse(c1);
1307-
final int l4 = _bitReverse(c2);
1308-
1309-
return l1.toRadixString(16).padLeft(2, '0').toUpperCase() +
1310-
l2.toRadixString(16).padLeft(2, '0').toUpperCase() +
1311-
l3.toRadixString(16).padLeft(2, '0').toUpperCase() +
1312-
l4.toRadixString(16).padLeft(2, '0').toUpperCase();
1304+
return a1.toRadixString(16).padLeft(2, '0').toUpperCase() +
1305+
a2.toRadixString(16).padLeft(2, '0').toUpperCase() +
1306+
c1.toRadixString(16).padLeft(2, '0').toUpperCase() +
1307+
c2.toRadixString(16).padLeft(2, '0').toUpperCase();
13131308
}
13141309

13151310
int _bitReverse(int x) {

lib/widgets/create_button.dart

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:irblaster_controller/ir/ir_protocol_registry.dart';
77
import 'package:irblaster_controller/ir/ir_protocol_types.dart';
88
import 'package:irblaster_controller/ir_finder/irblaster_db.dart';
99
import 'package:irblaster_controller/ir_finder/ir_finder_models.dart';
10+
import 'package:irblaster_controller/utils/button_color_accessibility.dart';
1011
import 'package:irblaster_controller/utils/ir.dart';
1112
import 'package:irblaster_controller/utils/remote.dart';
1213
import 'package:irblaster_controller/widgets/code_test.dart';
@@ -111,6 +112,7 @@ class _CreateButtonState extends State<CreateButton> {
111112
_selectedIcon = IconData(
112113
b.iconCodePoint!,
113114
fontFamily: b.iconFontFamily,
115+
fontPackage: b.iconFontPackage,
114116
);
115117
} else if (b.isImage) {
116118
_labelType = _LabelType.image;
@@ -126,7 +128,7 @@ class _CreateButtonState extends State<CreateButton> {
126128
}
127129

128130
if (b.buttonColor != null) {
129-
_selectedColor = Color(b.buttonColor!);
131+
_selectedColor = normalizeAccessibleButtonColor(Color(b.buttonColor!));
130132
}
131133

132134
if (b.protocol != null && b.protocol!.trim().isNotEmpty) {
@@ -786,18 +788,31 @@ class _CreateButtonState extends State<CreateButton> {
786788
}
787789

788790
Widget _colorOption(Color? color, String label, ThemeData theme) {
789-
final isSelected = _selectedColor == color;
790-
final displayColor = color ?? theme.colorScheme.surfaceContainerHighest;
791-
792-
return InkWell(
791+
final normalizedOption =
792+
color == null ? null : normalizeAccessibleButtonColor(color);
793+
final isSelected = normalizedOption == null
794+
? _selectedColor == null
795+
: _selectedColor?.value == normalizedOption.value;
796+
final displayColor =
797+
normalizedOption ?? theme.colorScheme.surfaceContainerHighest;
798+
final checkColor = normalizedOption == null
799+
? theme.colorScheme.onSurface
800+
: resolveButtonForeground(normalizedOption, Colors.white);
801+
802+
return Semantics(
803+
button: true,
804+
label: isSelected
805+
? 'Button color $label, selected'
806+
: 'Button color $label',
807+
child: InkWell(
793808
onTap: () {
794809
setState(() {
795-
_selectedColor = color;
810+
_selectedColor = normalizedOption;
796811
});
797812
},
798813
borderRadius: BorderRadius.circular(8),
799814
child: Container(
800-
width: 60,
815+
constraints: const BoxConstraints(minWidth: 60, minHeight: 60),
801816
padding: const EdgeInsets.symmetric(vertical: 8),
802817
child: Column(
803818
mainAxisSize: MainAxisSize.min,
@@ -818,9 +833,7 @@ class _CreateButtonState extends State<CreateButton> {
818833
child: isSelected
819834
? Icon(
820835
Icons.check,
821-
color: color == null
822-
? theme.colorScheme.onSurface
823-
: Colors.white,
836+
color: checkColor,
824837
size: 20,
825838
)
826839
: null,
@@ -830,15 +843,20 @@ class _CreateButtonState extends State<CreateButton> {
830843
label,
831844
style: theme.textTheme.bodySmall?.copyWith(fontSize: 10),
832845
textAlign: TextAlign.center,
833-
maxLines: 1,
846+
maxLines: 2,
834847
overflow: TextOverflow.ellipsis,
835848
),
836849
],
837850
),
838851
),
852+
),
839853
);
840854
}
841855

856+
int? get _selectedButtonColorValue => _selectedColor == null
857+
? null
858+
: normalizeAccessibleButtonColor(_selectedColor!).value;
859+
842860
List<Widget> _buildSignalSummaryChips() {
843861
final List<Widget> chips = [];
844862

@@ -911,7 +929,8 @@ class _CreateButtonState extends State<CreateButton> {
911929
necBitOrder: bitOrder,
912930
iconCodePoint: _labelType == _LabelType.icon ? _selectedIcon?.codePoint : null,
913931
iconFontFamily: _labelType == _LabelType.icon ? _selectedIcon?.fontFamily : null,
914-
buttonColor: _selectedColor?.value,
932+
iconFontPackage: _labelType == _LabelType.icon ? _selectedIcon?.fontPackage : null,
933+
buttonColor: _selectedButtonColorValue,
915934
);
916935
}
917936

@@ -926,7 +945,8 @@ class _CreateButtonState extends State<CreateButton> {
926945
isImage: _labelType == _LabelType.image,
927946
iconCodePoint: _labelType == _LabelType.icon ? _selectedIcon?.codePoint : null,
928947
iconFontFamily: _labelType == _LabelType.icon ? _selectedIcon?.fontFamily : null,
929-
buttonColor: _selectedColor?.value,
948+
iconFontPackage: _labelType == _LabelType.icon ? _selectedIcon?.fontPackage : null,
949+
buttonColor: _selectedButtonColorValue,
930950
);
931951
}
932952

@@ -968,7 +988,8 @@ class _CreateButtonState extends State<CreateButton> {
968988
protocolParams: params,
969989
iconCodePoint: _labelType == _LabelType.icon ? _selectedIcon?.codePoint : null,
970990
iconFontFamily: _labelType == _LabelType.icon ? _selectedIcon?.fontFamily : null,
971-
buttonColor: _selectedColor?.value,
991+
iconFontPackage: _labelType == _LabelType.icon ? _selectedIcon?.fontPackage : null,
992+
buttonColor: _selectedButtonColorValue,
972993
);
973994
}
974995

@@ -2589,7 +2610,8 @@ class _CreateButtonState extends State<CreateButton> {
25892610
necBitOrder: bitOrder,
25902611
iconCodePoint: _labelType == _LabelType.icon ? _selectedIcon?.codePoint : null,
25912612
iconFontFamily: _labelType == _LabelType.icon ? _selectedIcon?.fontFamily : null,
2592-
buttonColor: _selectedColor?.value,
2613+
iconFontPackage: _labelType == _LabelType.icon ? _selectedIcon?.fontPackage : null,
2614+
buttonColor: _selectedButtonColorValue,
25932615
);
25942616

25952617
Navigator.pop(context, button);
@@ -2613,7 +2635,8 @@ class _CreateButtonState extends State<CreateButton> {
26132635
isImage: _labelType == _LabelType.image,
26142636
iconCodePoint: _labelType == _LabelType.icon ? _selectedIcon?.codePoint : null,
26152637
iconFontFamily: _labelType == _LabelType.icon ? _selectedIcon?.fontFamily : null,
2616-
buttonColor: _selectedColor?.value,
2638+
iconFontPackage: _labelType == _LabelType.icon ? _selectedIcon?.fontPackage : null,
2639+
buttonColor: _selectedButtonColorValue,
26172640
);
26182641

26192642
Navigator.pop(context, button);
@@ -2667,7 +2690,8 @@ class _CreateButtonState extends State<CreateButton> {
26672690
protocolParams: params,
26682691
iconCodePoint: _labelType == _LabelType.icon ? _selectedIcon?.codePoint : null,
26692692
iconFontFamily: _labelType == _LabelType.icon ? _selectedIcon?.fontFamily : null,
2670-
buttonColor: _selectedColor?.value,
2693+
iconFontPackage: _labelType == _LabelType.icon ? _selectedIcon?.fontPackage : null,
2694+
buttonColor: _selectedButtonColorValue,
26712695
);
26722696

26732697
Navigator.pop(context, button);

0 commit comments

Comments
 (0)