Skip to content

Commit ebd620e

Browse files
Merge pull request #17 from GeorgeEnglezos/feature/code-cleanup
code cleanup / scripts in drawer
2 parents fd6678d + 78fc988 commit ebd620e

33 files changed

Lines changed: 2122 additions & 2736 deletions

.github/workflows/build.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ on:
44
push:
55
tags:
66
- 'v*'
7-
pull_request:
8-
branches: [ main ]
97
schedule:
108
# 21:00 UTC = 23:00 Europe/Athens — runs on days 1, 5, 9, 13, 17, 21, 25, 29 (every ~4 days)
119
- cron: '0 21 1,5,9,13,17,21,25,29 * *'

Docs/CODEBASE_REFERENCE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# API Reference
22

33
Developer documentation for the Scrcpy GUI Flutter codebase.
4-
App version: **1.7.0** · Dart SDK: **^3.9.2**
4+
App version: **1.7.1** · Dart SDK: **^3.9.2**
55

66
---
77

ScrcpyGui/lib/main.dart

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import 'pages/home_page.dart';
1818
import 'pages/logs_page.dart';
1919
import 'services/app_icon_controller.dart';
2020
import 'services/log_service.dart';
21-
import 'services/command_builder_service.dart';
21+
import 'services/command_notifier.dart';
2222
import 'services/device_manager_service.dart';
2323
import 'services/settings_service.dart';
2424
import 'theme/app_colors.dart';
@@ -57,9 +57,10 @@ Future<void> main() async {
5757
final deviceManager = DeviceManagerService();
5858
await deviceManager.initialize();
5959

60-
// Initialize CommandBuilderService with reference to DeviceManagerService
61-
final commandBuilder = CommandBuilderService();
62-
commandBuilder.deviceManagerService = deviceManager;
60+
// Initialize CommandNotifier — unified state for the new architecture
61+
final commandNotifier = CommandNotifier();
62+
commandNotifier.setDeviceManager(deviceManager);
63+
commandNotifier.loadDefault();
6364

6465
final iconController = AppIconController(
6566
appDrawerSettings: appDrawerSettings,
@@ -71,9 +72,7 @@ Future<void> main() async {
7172
ChangeNotifierProvider<DeviceManagerService>.value(
7273
value: deviceManager,
7374
),
74-
ChangeNotifierProvider<CommandBuilderService>.value(
75-
value: commandBuilder,
76-
),
75+
ChangeNotifierProvider<CommandNotifier>.value(value: commandNotifier),
7776
ChangeNotifierProvider<AppIconController>.value(value: iconController),
7877
ChangeNotifierProvider<LogService>.value(value: LogService.instance),
7978
],
@@ -105,20 +104,50 @@ class _ScrcpyGuiAppState extends State<ScrcpyGuiApp> {
105104
/// Currently selected page index (0: Home, 1: Favorites, 2: Resources, 3: Settings)
106105
late int selectedIndex;
107106
late AppSettings _currentSettings;
108-
final SettingsService _settingsService = SettingsService();
109107
UpdateService? _updateResult;
110108
bool _hideBanner = false;
111109

112110
@override
113111
void initState() {
114112
super.initState();
115113
_currentSettings = widget.settings;
116-
// Set initial tab based on bootTab setting
117114
selectedIndex = _getInitialTabIndex();
118-
_startSettingsPolling();
115+
SettingsService().appSettingsNotifier.addListener(_onSettingsChanged);
119116
_checkUpdateOnStartup();
120117
}
121118

119+
@override
120+
void dispose() {
121+
SettingsService().appSettingsNotifier.removeListener(_onSettingsChanged);
122+
super.dispose();
123+
}
124+
125+
void _onSettingsChanged() {
126+
final newSettings = SettingsService.currentSettings;
127+
if (newSettings == null || !mounted) return;
128+
129+
final tabsVisibilityChanged =
130+
newSettings.showBatFilesTab != _currentSettings.showBatFilesTab ||
131+
newSettings.showAppDrawerTab != _currentSettings.showAppDrawerTab ||
132+
newSettings.loggingEnabled != _currentSettings.loggingEnabled;
133+
134+
int newIndex = selectedIndex;
135+
if (tabsVisibilityChanged) {
136+
final currentTabs = _visibleTabLabelsFor(_currentSettings);
137+
final label = selectedIndex < currentTabs.length
138+
? currentTabs[selectedIndex]
139+
: 'Home';
140+
final newTabs = _visibleTabLabelsFor(newSettings);
141+
final idx = newTabs.indexOf(label);
142+
newIndex = idx >= 0 ? idx : 0;
143+
}
144+
145+
setState(() {
146+
_currentSettings = newSettings;
147+
selectedIndex = newIndex;
148+
});
149+
}
150+
122151
Future<void> _checkUpdateOnStartup() async {
123152
if (!_currentSettings.checkForUpdatesOnStartup) return;
124153

@@ -160,36 +189,6 @@ class _ScrcpyGuiAppState extends State<ScrcpyGuiApp> {
160189
];
161190
}
162191

163-
void _startSettingsPolling() {
164-
// Poll for settings changes every 500ms
165-
Future.delayed(const Duration(milliseconds: 500), () async {
166-
if (mounted) {
167-
final newSettings = await _settingsService.loadSettings();
168-
final tabsVisibilityChanged =
169-
newSettings.showBatFilesTab != _currentSettings.showBatFilesTab ||
170-
newSettings.showAppDrawerTab != _currentSettings.showAppDrawerTab ||
171-
newSettings.loggingEnabled != _currentSettings.loggingEnabled;
172-
173-
if (tabsVisibilityChanged) {
174-
final currentTabs = _visibleTabLabelsFor(_currentSettings);
175-
final currentTabLabel =
176-
selectedIndex >= 0 && selectedIndex < currentTabs.length
177-
? currentTabs[selectedIndex]
178-
: 'Home';
179-
180-
final newTabs = _visibleTabLabelsFor(newSettings);
181-
final newIndex = newTabs.indexOf(currentTabLabel);
182-
183-
setState(() {
184-
_currentSettings = newSettings;
185-
selectedIndex = newIndex >= 0 ? newIndex : 0;
186-
});
187-
}
188-
_startSettingsPolling();
189-
}
190-
});
191-
}
192-
193192
/// List of available pages in the application
194193
///
195194
/// Index mapping (when Scripts tab is shown):
@@ -207,8 +206,11 @@ class _ScrcpyGuiAppState extends State<ScrcpyGuiApp> {
207206
List<Widget> get pages => [
208207
HomePage(
209208
panelOrder: _currentSettings.panelOrder,
210-
onNavigateToSettings: () =>
211-
setState(() => selectedIndex = pages.length - 1),
209+
onNavigateToSettings: () {
210+
final tabs = _visibleTabLabelsFor(_currentSettings);
211+
final idx = tabs.indexOf('Settings');
212+
if (idx >= 0) setState(() => selectedIndex = idx);
213+
},
212214
),
213215
const FavoritesPage(),
214216
if (_currentSettings.showAppDrawerTab) const AppDrawerPage(),
@@ -235,13 +237,14 @@ class _ScrcpyGuiAppState extends State<ScrcpyGuiApp> {
235237
showAppDrawerTab: _currentSettings.showAppDrawerTab,
236238
showLogsTab: _currentSettings.loggingEnabled,
237239
onItemSelected: (index) {
238-
// Clear command builder when leaving Home page (index 0)
240+
final notifier = Provider.of<CommandNotifier>(
241+
context,
242+
listen: false,
243+
);
239244
if (selectedIndex == 0 && index != 0) {
240-
final commandService = Provider.of<CommandBuilderService>(
241-
context,
242-
listen: false,
243-
);
244-
commandService.resetToDefaults();
245+
notifier.reset();
246+
} else if (index == 0 && selectedIndex != 0) {
247+
notifier.loadDefault();
245248
}
246249
setState(() => selectedIndex = index);
247250
},

ScrcpyGui/lib/models/app_drawer_settings_model.dart

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,37 @@
11
import 'dart:convert';
22
import '../services/icon_fetch_strategy.dart';
33

4+
/// A user-defined or auto-generated grouping of apps. Immutable — use
5+
/// [copyWith] to derive a new instance.
46
class AppGroup {
5-
String name;
6-
List<String> items;
7-
bool isAutoGenerated;
8-
bool collapsed;
7+
final String name;
8+
final List<String> items;
9+
final bool isAutoGenerated;
10+
final bool collapsed;
911

1012
AppGroup({
1113
required this.name,
12-
this.items = const [],
14+
List<String> items = const [],
1315
this.isAutoGenerated = false,
1416
this.collapsed = false,
15-
});
17+
}) : items = List.unmodifiable(items);
18+
19+
AppGroup copyWith({
20+
String? name,
21+
List<String>? items,
22+
bool? isAutoGenerated,
23+
bool? collapsed,
24+
}) =>
25+
AppGroup(
26+
name: name ?? this.name,
27+
items: items ?? this.items,
28+
isAutoGenerated: isAutoGenerated ?? this.isAutoGenerated,
29+
collapsed: collapsed ?? this.collapsed,
30+
);
1631

1732
factory AppGroup.fromJson(Map<String, dynamic> json) => AppGroup(
1833
name: json['name'] as String,
19-
items: (json['items'] as List<dynamic>?)?.cast<String>() ?? [],
34+
items: (json['items'] as List<dynamic>?)?.cast<String>() ?? const [],
2035
isAutoGenerated: json['isAutoGenerated'] as bool? ?? false,
2136
collapsed: json['collapsed'] as bool? ?? false,
2237
);
@@ -29,15 +44,17 @@ class AppGroup {
2944
};
3045
}
3146

47+
/// App Drawer settings. Immutable — use [copyWith] to derive a new instance.
3248
class AppDrawerSettings {
33-
String appLaunchCommand;
34-
IconFetchMethod iconFetchMethod;
35-
List<String> favorites;
36-
List<AppGroup> groups;
37-
bool autoGroupByCategory;
38-
bool showScripts;
39-
bool otherCollapsed;
40-
bool scriptsCollapsed;
49+
final String appLaunchCommand;
50+
final IconFetchMethod iconFetchMethod;
51+
final List<String> favorites;
52+
final List<AppGroup> groups;
53+
final bool autoGroupByCategory;
54+
final bool showScripts;
55+
final bool otherCollapsed;
56+
final bool scriptsCollapsed;
57+
final bool includeSystemApps;
4158

4259
static const String _defaultCommand =
4360
'scrcpy --pause-on-exit=if-error --new-display=1920x1080';
@@ -51,8 +68,32 @@ class AppDrawerSettings {
5168
this.showScripts = false,
5269
this.otherCollapsed = false,
5370
this.scriptsCollapsed = false,
54-
}) : favorites = favorites ?? [],
55-
groups = groups ?? [];
71+
this.includeSystemApps = false,
72+
}) : favorites = List.unmodifiable(favorites ?? const []),
73+
groups = List.unmodifiable(groups ?? const []);
74+
75+
AppDrawerSettings copyWith({
76+
String? appLaunchCommand,
77+
IconFetchMethod? iconFetchMethod,
78+
List<String>? favorites,
79+
List<AppGroup>? groups,
80+
bool? autoGroupByCategory,
81+
bool? showScripts,
82+
bool? otherCollapsed,
83+
bool? scriptsCollapsed,
84+
bool? includeSystemApps,
85+
}) =>
86+
AppDrawerSettings(
87+
appLaunchCommand: appLaunchCommand ?? this.appLaunchCommand,
88+
iconFetchMethod: iconFetchMethod ?? this.iconFetchMethod,
89+
favorites: favorites ?? this.favorites,
90+
groups: groups ?? this.groups,
91+
autoGroupByCategory: autoGroupByCategory ?? this.autoGroupByCategory,
92+
showScripts: showScripts ?? this.showScripts,
93+
otherCollapsed: otherCollapsed ?? this.otherCollapsed,
94+
scriptsCollapsed: scriptsCollapsed ?? this.scriptsCollapsed,
95+
includeSystemApps: includeSystemApps ?? this.includeSystemApps,
96+
);
5697

5798
factory AppDrawerSettings.fromJson(Map<String, dynamic> json) =>
5899
AppDrawerSettings(
@@ -61,16 +102,17 @@ class AppDrawerSettings {
61102
iconFetchMethod:
62103
iconFetchMethodFromString(json['iconFetchMethod'] as String?),
63104
favorites:
64-
(json['favorites'] as List<dynamic>?)?.cast<String>() ?? [],
105+
(json['favorites'] as List<dynamic>?)?.cast<String>() ?? const [],
65106
groups: (json['groups'] as List<dynamic>?)
66107
?.map((e) => AppGroup.fromJson(e as Map<String, dynamic>))
67108
.toList() ??
68-
[],
109+
const [],
69110
autoGroupByCategory:
70111
json['autoGroupByCategory'] as bool? ?? true,
71112
showScripts: json['showScripts'] as bool? ?? false,
72113
otherCollapsed: json['otherCollapsed'] as bool? ?? false,
73114
scriptsCollapsed: json['scriptsCollapsed'] as bool? ?? false,
115+
includeSystemApps: json['includeSystemApps'] as bool? ?? false,
74116
);
75117

76118
Map<String, dynamic> toJson() => {
@@ -82,6 +124,7 @@ class AppDrawerSettings {
82124
'showScripts': showScripts,
83125
'otherCollapsed': otherCollapsed,
84126
'scriptsCollapsed': scriptsCollapsed,
127+
'includeSystemApps': includeSystemApps,
85128
};
86129

87130
String toJsonString() => jsonEncode(toJson());

0 commit comments

Comments
 (0)