Skip to content

Commit b240f53

Browse files
Multiple device support fix
1 parent a46093a commit b240f53

5 files changed

Lines changed: 305 additions & 12 deletions

File tree

.claude/settings.local.json

Lines changed: 0 additions & 8 deletions
This file was deleted.

.github/workflows/build.yml

Lines changed: 263 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,273 @@ jobs:
6464
- name: Build Linux
6565
run: flutter build linux --release
6666

67+
- name: Create Linux installation package
68+
run: |
69+
# Set the app name
70+
APP_NAME="scrcpy_gui_prod"
71+
BUILD_PATH="build/linux/x64/release/bundle"
72+
73+
# Create package directory
74+
mkdir -p linux_package
75+
76+
# Copy the entire bundle
77+
cp -r "${BUILD_PATH}" "linux_package/${APP_NAME}"
78+
79+
# Create desktop entry file
80+
cat > linux_package/${APP_NAME}.desktop << 'DESKTOP_EOF'
81+
[Desktop Entry]
82+
Name=Scrcpy GUI
83+
Comment=A GUI for Scrcpy - Android screen mirroring
84+
Exec=scrcpy_gui_prod
85+
Icon=scrcpy_gui_prod
86+
Terminal=false
87+
Type=Application
88+
Categories=Utility;
89+
DESKTOP_EOF
90+
91+
# Create install script
92+
cat > linux_package/install.sh << 'INSTALL_EOF'
93+
#!/bin/bash
94+
95+
# Linux App Installer for scrcpy_gui_prod
96+
# This script installs the app and creates desktop integration
97+
98+
set -e
99+
100+
APP_NAME="scrcpy_gui_prod"
101+
INSTALL_DIR="$HOME/.local/share/${APP_NAME}"
102+
BIN_DIR="$HOME/.local/bin"
103+
DESKTOP_DIR="$HOME/.local/share/applications"
104+
ICON_DIR="$HOME/.local/share/icons/hicolor/256x256/apps"
105+
106+
echo "======================================"
107+
echo " Linux App Installer"
108+
echo "======================================"
109+
echo ""
110+
111+
# Check if app bundle exists
112+
if [ ! -d "$APP_NAME" ]; then
113+
echo "Error: $APP_NAME directory not found"
114+
echo "Please run this script from the extracted folder"
115+
exit 1
116+
fi
117+
118+
# Check for required dependencies
119+
echo "Step 1: Checking dependencies..."
120+
if ! dpkg -l | grep -q libgtk-3-0; then
121+
echo "Warning: libgtk-3-0 not found. Installing..."
122+
echo "You may need to enter your password:"
123+
sudo apt-get update
124+
sudo apt-get install -y libgtk-3-0
125+
fi
126+
echo "✓ Dependencies satisfied"
127+
echo ""
128+
129+
# Create directories
130+
echo "Step 2: Creating directories..."
131+
mkdir -p "$INSTALL_DIR"
132+
mkdir -p "$BIN_DIR"
133+
mkdir -p "$DESKTOP_DIR"
134+
mkdir -p "$ICON_DIR"
135+
echo "✓ Directories created"
136+
echo ""
137+
138+
# Copy application files
139+
echo "Step 3: Installing application..."
140+
if [ -d "$INSTALL_DIR" ]; then
141+
echo "Removing old installation..."
142+
rm -rf "$INSTALL_DIR"
143+
fi
144+
cp -r "$APP_NAME" "$INSTALL_DIR/"
145+
chmod +x "$INSTALL_DIR/${APP_NAME}/${APP_NAME}"
146+
echo "✓ Application installed to $INSTALL_DIR"
147+
echo ""
148+
149+
# Create symlink in bin directory
150+
echo "Step 4: Creating launcher..."
151+
ln -sf "$INSTALL_DIR/${APP_NAME}/${APP_NAME}" "$BIN_DIR/${APP_NAME}"
152+
echo "✓ Launcher created in $BIN_DIR"
153+
echo ""
154+
155+
# Install icon if it exists
156+
echo "Step 5: Installing icon..."
157+
if [ -f "$APP_NAME/data/flutter_assets/icon.png" ]; then
158+
cp "$APP_NAME/data/flutter_assets/icon.png" "$ICON_DIR/${APP_NAME}.png"
159+
echo "✓ Icon installed"
160+
else
161+
echo "⚠ Icon not found, skipping"
162+
fi
163+
echo ""
164+
165+
# Install desktop entry
166+
echo "Step 6: Creating desktop entry..."
167+
cat > "$DESKTOP_DIR/${APP_NAME}.desktop" << DESKTOP_EOF
168+
[Desktop Entry]
169+
Name=Scrcpy GUI
170+
Comment=A GUI for Scrcpy - Android screen mirroring
171+
Exec=$BIN_DIR/${APP_NAME}
172+
Icon=${APP_NAME}
173+
Terminal=false
174+
Type=Application
175+
Categories=Utility;
176+
DESKTOP_EOF
177+
178+
chmod +x "$DESKTOP_DIR/${APP_NAME}.desktop"
179+
echo "✓ Desktop entry created"
180+
echo ""
181+
182+
# Update desktop database
183+
echo "Step 7: Updating desktop database..."
184+
if command -v update-desktop-database &> /dev/null; then
185+
update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true
186+
fi
187+
echo "✓ Database updated"
188+
echo ""
189+
190+
echo "======================================"
191+
echo " Installation Complete!"
192+
echo "======================================"
193+
echo ""
194+
echo "The app has been installed to:"
195+
echo " $INSTALL_DIR"
196+
echo ""
197+
echo "You can now run it by:"
198+
echo " - Searching for 'Scrcpy GUI' in your application menu"
199+
echo " - Running: $APP_NAME"
200+
echo " - Running: $BIN_DIR/${APP_NAME}"
201+
echo ""
202+
echo "To uninstall, run: ./uninstall.sh"
203+
echo ""
204+
INSTALL_EOF
205+
206+
# Make install script executable
207+
chmod +x linux_package/install.sh
208+
209+
# Create uninstall script
210+
cat > linux_package/uninstall.sh << 'UNINSTALL_EOF'
211+
#!/bin/bash
212+
213+
# Uninstaller for scrcpy_gui_prod
214+
215+
APP_NAME="scrcpy_gui_prod"
216+
INSTALL_DIR="$HOME/.local/share/${APP_NAME}"
217+
BIN_DIR="$HOME/.local/bin"
218+
DESKTOP_DIR="$HOME/.local/share/applications"
219+
ICON_DIR="$HOME/.local/share/icons/hicolor/256x256/apps"
220+
221+
echo "======================================"
222+
echo " Uninstalling Scrcpy GUI"
223+
echo "======================================"
224+
echo ""
225+
226+
# Remove application files
227+
if [ -d "$INSTALL_DIR" ]; then
228+
echo "Removing application files..."
229+
rm -rf "$INSTALL_DIR"
230+
echo "✓ Application files removed"
231+
fi
232+
233+
# Remove symlink
234+
if [ -L "$BIN_DIR/${APP_NAME}" ]; then
235+
echo "Removing launcher..."
236+
rm "$BIN_DIR/${APP_NAME}"
237+
echo "✓ Launcher removed"
238+
fi
239+
240+
# Remove desktop entry
241+
if [ -f "$DESKTOP_DIR/${APP_NAME}.desktop" ]; then
242+
echo "Removing desktop entry..."
243+
rm "$DESKTOP_DIR/${APP_NAME}.desktop"
244+
echo "✓ Desktop entry removed"
245+
fi
246+
247+
# Remove icon
248+
if [ -f "$ICON_DIR/${APP_NAME}.png" ]; then
249+
echo "Removing icon..."
250+
rm "$ICON_DIR/${APP_NAME}.png"
251+
echo "✓ Icon removed"
252+
fi
253+
254+
# Update desktop database
255+
if command -v update-desktop-database &> /dev/null; then
256+
update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true
257+
fi
258+
259+
echo ""
260+
echo "======================================"
261+
echo " Uninstall Complete!"
262+
echo "======================================"
263+
echo ""
264+
UNINSTALL_EOF
265+
266+
# Make uninstall script executable
267+
chmod +x linux_package/uninstall.sh
268+
269+
# Create README
270+
cat > linux_package/README.txt << 'README_EOF'
271+
Linux Installation Instructions
272+
=================================
273+
274+
EASY INSTALLATION (Recommended):
275+
---------------------------------
276+
1. Open Terminal in this directory
277+
2. Run: chmod +x install.sh
278+
3. Run: ./install.sh
279+
4. The app will be installed and available in your application menu!
280+
281+
ALTERNATIVE - One-line installation:
282+
------------------------------------
283+
Copy and paste this into Terminal:
284+
285+
chmod +x install.sh && ./install.sh
286+
287+
MANUAL INSTALLATION:
288+
--------------------
289+
1. Extract the scrcpy_gui_prod folder to your desired location
290+
2. Run: chmod +x scrcpy_gui_prod/scrcpy_gui_prod
291+
3. Run: ./scrcpy_gui_prod/scrcpy_gui_prod
292+
293+
UNINSTALLATION:
294+
---------------
295+
Run: ./uninstall.sh
296+
297+
REQUIREMENTS:
298+
-------------
299+
- GTK3 libraries (usually pre-installed)
300+
- If missing, install with: sudo apt-get install libgtk-3-0
301+
302+
WHAT DOES THE INSTALLER DO?
303+
----------------------------
304+
- Installs the app to ~/.local/share/scrcpy_gui_prod
305+
- Creates a launcher in ~/.local/bin/scrcpy_gui_prod
306+
- Adds desktop entry for application menu integration
307+
- Installs the application icon
308+
- Checks and installs required dependencies
309+
310+
TROUBLESHOOTING:
311+
----------------
312+
If the app doesn't appear in your menu after installation:
313+
- Log out and log back in
314+
- Or run: update-desktop-database ~/.local/share/applications
315+
316+
If you get permission errors:
317+
- Make sure the install.sh script is executable
318+
- Some distributions may require different installation paths
319+
README_EOF
320+
321+
# Move to artifacts directory
322+
mkdir -p artifacts
323+
mv linux_package artifacts/
324+
325+
# Show package contents
326+
echo "Package contents:"
327+
ls -lh artifacts/linux_package/
328+
67329
- name: Archive Linux build
68330
uses: actions/upload-artifact@v4
69331
with:
70332
name: linux-build
71-
path: ScrcpyGui/build/linux/x64/release/bundle/
333+
path: ScrcpyGui/artifacts/linux_package/
72334

73335
build-macos:
74336
runs-on: macos-latest

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ app.*.symbols
174174
# VS Code
175175
.vscode/
176176

177+
# =============================================================================
178+
# Claude Code
179+
# =============================================================================
180+
.claude/
181+
177182
# =============================================================================
178183
# Miscellaneous
179184
# =============================================================================

ScrcpyGui/lib/main.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ Future<void> main() async {
4242
final deviceManager = DeviceManagerService();
4343
await deviceManager.initialize();
4444

45+
// Initialize CommandBuilderService with reference to DeviceManagerService
46+
final commandBuilder = CommandBuilderService();
47+
commandBuilder.deviceManagerService = deviceManager;
48+
4549
// Load settings
4650
final settingsService = SettingsService();
4751
final settings = await settingsService.loadSettings();
@@ -52,7 +56,9 @@ Future<void> main() async {
5256
ChangeNotifierProvider<DeviceManagerService>.value(
5357
value: deviceManager,
5458
),
55-
ChangeNotifierProvider(create: (_) => CommandBuilderService()),
59+
ChangeNotifierProvider<CommandBuilderService>.value(
60+
value: commandBuilder,
61+
),
5662
],
5763
child: ScrcpyGuiApp(settings: settings),
5864
),

ScrcpyGui/lib/services/command_builder_service.dart

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,33 @@ class CommandBuilderService extends ChangeNotifier {
6060
OtgModeOptions otgModeOptions = OtgModeOptions();
6161

6262
/// Reference to DeviceManagerService to get selected device
63-
DeviceManagerService? deviceManagerService;
63+
DeviceManagerService? _deviceManagerService;
64+
65+
/// Gets the device manager service reference
66+
DeviceManagerService? get deviceManagerService => _deviceManagerService;
67+
68+
/// Sets the device manager service and listens for device selection changes
69+
set deviceManagerService(DeviceManagerService? service) {
70+
// Remove old listener if exists
71+
_deviceManagerService?.removeListener(_onDeviceChanged);
72+
73+
_deviceManagerService = service;
74+
75+
// Add new listener if service is not null
76+
_deviceManagerService?.addListener(_onDeviceChanged);
77+
}
78+
79+
/// Called when device selection changes in DeviceManagerService
80+
void _onDeviceChanged() {
81+
notifyListeners(); // Rebuild command when device changes
82+
}
83+
84+
@override
85+
void dispose() {
86+
// Clean up listener
87+
_deviceManagerService?.removeListener(_onDeviceChanged);
88+
super.dispose();
89+
}
6490

6591
void updateAudioOptions(AudioOptions options) {
6692
audioOptions = options;
@@ -154,7 +180,9 @@ class CommandBuilderService extends ChangeNotifier {
154180

155181
// Add device serial if selected
156182
final deviceSerial = deviceManagerService?.selectedDevice;
157-
final serialPart = deviceSerial != null ? '--serial=$deviceSerial' : '';
183+
final serialPart = (deviceSerial != null && deviceSerial.isNotEmpty)
184+
? '--serial=$deviceSerial'
185+
: '';
158186

159187
final parts = [
160188
baseCommand,

0 commit comments

Comments
 (0)