diff --git a/.gitignore b/.gitignore index bb9cf3e..50557bc 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,5 @@ dist/ *.dmg # Snap package *.snap +# Snap debugging +squashfs-root/ diff --git a/assets/i18n/en.json b/assets/i18n/en.json index b0af183..3cc31fa 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -110,6 +110,7 @@ "serialCopied": "Device serial copied to clipboard" }, "settings": { + "snapEnvironmentHint": "Executable paths are preset when running in snap", "adbExecutable": { "title": "adb executable", "dialogTitle": "adb scrcpy executable", diff --git a/lib/application/objectbox.dart b/lib/application/objectbox.dart index 2084f5c..a8abb82 100644 --- a/lib/application/objectbox.dart +++ b/lib/application/objectbox.dart @@ -1,7 +1,6 @@ import 'dart:io'; -import 'package:path/path.dart' as path; -import 'package:path_provider/path_provider.dart'; +import 'package:scrcpy_buddy/service/snap_environment.dart'; import '../objectbox.g.dart'; // created by `flutter pub run build_runner build` import 'model/profile.dart'; @@ -14,11 +13,9 @@ class ObjectBox { // Add any additional setup code, e.g. build queries. } - static Future create() async { - // Get snap-safe directory - final String dbPath = await _getStoragePath(); + static Future create(SnapEnvironment snapEnvironment) async { + final String dbPath = await snapEnvironment.getStoragePath(); - // Ensure directory exists final dir = Directory(dbPath); if (!dir.existsSync()) { await dir.create(recursive: true); @@ -29,22 +26,4 @@ class ObjectBox { } Box get profileBox => store.box(); - - static Future _getStoragePath() async { - // Check if running in snap - final snapUserCommon = Platform.environment['SNAP_USER_COMMON']; - final snapUserData = Platform.environment['SNAP_USER_DATA']; - - if (snapUserCommon != null) { - // Use SNAP_USER_COMMON for persistent data across snap revisions - return path.join(snapUserCommon, 'scrcpy_buddy'); - } else if (snapUserData != null) { - // Use SNAP_USER_DATA (backed up on snap refresh) - return path.join(snapUserData, 'scrcpy_buddy'); - } else { - // Not running as snap, use default location - final dir = await getApplicationSupportDirectory(); - return path.join(dir.path, "scrcpy_buddy_db"); - } - } } diff --git a/lib/init.dart b/lib/init.dart index 45f2fb9..c7ed4ed 100644 --- a/lib/init.dart +++ b/lib/init.dart @@ -4,6 +4,7 @@ import 'package:fluent_ui/fluent_ui.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_acrylic/flutter_acrylic.dart' as flutter_acrylic; import 'package:scrcpy_buddy/main.reflectable.dart'; +import 'package:scrcpy_buddy/service/snap_environment.dart'; import 'package:system_theme/system_theme.dart'; import 'package:tray_manager/tray_manager.dart'; import 'package:window_manager/window_manager.dart'; @@ -39,7 +40,11 @@ Future initTrayIcon([Brightness platformBrightness = Brightness.light]) as // tray icon init try { final iconName = platformBrightness == Brightness.dark ? 'icon_light' : 'icon_dark'; - await trayManager.setIcon(Platform.isWindows ? 'assets/tray/$iconName.ico' : 'assets/tray/$iconName.png'); + final snapIconPath = SnapEnvironment().getSnapTrayIconPath(iconName); + final iconPath = + snapIconPath ?? + (Platform.isWindows ? 'assets/tray/$iconName.ico' : 'assets/tray/$iconName.png'); // fallback for non-snap + await trayManager.setIcon(iconPath); if (Platform.isLinux) { // Don't show title on macOS (takes up more space in menu bar) // Not supported on Windows diff --git a/lib/injector.dart b/lib/injector.dart index 8cbf550..df23b32 100644 --- a/lib/injector.dart +++ b/lib/injector.dart @@ -3,6 +3,7 @@ import 'package:provider/provider.dart'; import 'package:scrcpy_buddy/service/adb_service.dart'; import 'package:scrcpy_buddy/service/running_process_manager.dart'; import 'package:scrcpy_buddy/service/scrcpy_service.dart'; +import 'package:scrcpy_buddy/service/snap_environment.dart'; import 'application/adb_result_parser.dart'; @@ -16,7 +17,11 @@ List get providers => [ create: (context) => AdbService(context.read(), context.read()), ), - Provider(create: (context) => ScrcpyService(context.read())), + Provider(create: (_) => SnapEnvironment()), + + Provider( + create: (context) => ScrcpyService(context.read(), context.read()), + ), Provider(create: (_) => RunningProcessManager()), ]; diff --git a/lib/main.dart b/lib/main.dart index b795cae..f6bd6b6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,6 +14,7 @@ import 'package:scrcpy_buddy/init.dart'; import 'package:scrcpy_buddy/presentation/devices/bloc/devices_bloc.dart'; import 'package:scrcpy_buddy/presentation/search/bloc/search_bloc.dart'; import 'package:scrcpy_buddy/routes.dart'; +import 'package:scrcpy_buddy/service/snap_environment.dart'; import 'package:system_theme/system_theme.dart'; import 'package:window_manager/window_manager.dart'; @@ -30,7 +31,7 @@ late ObjectBox _objectBox; void main() async { await init(); await _prefs.initialize(); - _objectBox = await ObjectBox.create(); + _objectBox = await ObjectBox.create(SnapEnvironment()); runApp(const MyApp()); } diff --git a/lib/presentation/settings/widget/adb_executable_setting.dart b/lib/presentation/settings/widget/adb_executable_setting.dart index 5ac762c..d95cedc 100644 --- a/lib/presentation/settings/widget/adb_executable_setting.dart +++ b/lib/presentation/settings/widget/adb_executable_setting.dart @@ -10,6 +10,7 @@ import 'package:scrcpy_buddy/presentation/devices/bloc/devices_bloc.dart'; import 'package:scrcpy_buddy/presentation/extension/translation_extension.dart'; import 'package:scrcpy_buddy/presentation/widgets/app_widgets.dart'; import 'package:scrcpy_buddy/service/adb_service.dart'; +import 'package:scrcpy_buddy/service/snap_environment.dart'; class AdbExecutableSetting extends StatefulWidget { const AdbExecutableSetting({super.key}); @@ -25,6 +26,7 @@ class _AdbExecutableSettingState extends AppModuleState { late final _executablePreference = context.read().adbExecutable; late final _adbService = context.read(); late final _devicesBloc = context.read(); + late final _snapEnvironment = context.read(); final _infoFlyoutController = FlyoutController(); final _textController = TextEditingController(); @@ -96,26 +98,30 @@ class _AdbExecutableSettingState extends AppModuleState { @override Widget build(BuildContext context) { + final isSnap = _snapEnvironment.isRunningInSnap; return Row( children: [ Expanded( child: TextBox( controller: _textController, - enabled: !_isSaving, + enabled: !_isSaving && !isSnap, textInputAction: TextInputAction.done, + placeholder: isSnap ? context.translatedText(key: 'settings.snapEnvironmentHint') : null, onSubmitted: (value) => _validateAndSave(value), onTapOutside: (_) => _validateAndSave(_textController.text), suffix: _isSaving ? ProgressRing() : IconButton( icon: Icon(WindowsIcons.file_explorer), - onPressed: () async { - final result = await openFile(); + onPressed: isSnap + ? null + : () async { + final result = await openFile(); - if (result != null) { - _validateAndSave(result.path); - } - }, + if (result != null) { + _validateAndSave(result.path); + } + }, ), ), ), diff --git a/lib/presentation/settings/widget/scrcpy_executable_setting.dart b/lib/presentation/settings/widget/scrcpy_executable_setting.dart index 130c1cd..8fb2b29 100644 --- a/lib/presentation/settings/widget/scrcpy_executable_setting.dart +++ b/lib/presentation/settings/widget/scrcpy_executable_setting.dart @@ -8,6 +8,7 @@ import 'package:scrcpy_buddy/application/extension/scrcpy_error_extension.dart'; import 'package:scrcpy_buddy/presentation/extension/translation_extension.dart'; import 'package:scrcpy_buddy/presentation/widgets/app_widgets.dart'; import 'package:scrcpy_buddy/service/scrcpy_service.dart'; +import 'package:scrcpy_buddy/service/snap_environment.dart'; class ScrcpyExecutableSetting extends StatefulWidget { const ScrcpyExecutableSetting({super.key}); @@ -22,6 +23,7 @@ class _ScrcpyExecutableSettingState extends AppModuleState().scrcpyExecutable; late final _scrcpyService = context.read(); + late final _snapEnvironment = context.read(); final _infoFlyoutController = FlyoutController(); final _textController = TextEditingController(); @@ -85,26 +87,30 @@ class _ScrcpyExecutableSettingState extends AppModuleState _validateAndSave(value), onTapOutside: (_) => _validateAndSave(_textController.text), suffix: _isSaving ? ProgressRing() : IconButton( icon: Icon(WindowsIcons.file_explorer), - onPressed: () async { - final result = await openFile(); + onPressed: isSnap + ? null + : () async { + final result = await openFile(); - if (result != null) { - _validateAndSave(result.path); - } - }, + if (result != null) { + _validateAndSave(result.path); + } + }, ), ), ), diff --git a/lib/service/scrcpy_service.dart b/lib/service/scrcpy_service.dart index 061b2b7..7d65d12 100644 --- a/lib/service/scrcpy_service.dart +++ b/lib/service/scrcpy_service.dart @@ -6,11 +6,13 @@ import 'package:process/process.dart'; import 'package:scrcpy_buddy/application/model/scrcpy/device_app.dart'; import 'package:scrcpy_buddy/application/model/scrcpy/scrcpy_error.dart'; import 'package:scrcpy_buddy/application/model/scrcpy/scrcpy_result.dart'; +import 'package:scrcpy_buddy/service/snap_environment.dart'; class ScrcpyService { final ProcessManager _processManager; + final SnapEnvironment _snapEnvironment; - ScrcpyService(this._processManager); + ScrcpyService(this._processManager, this._snapEnvironment); Future start(String deviceSerial, String adbPath, String path, List args) async { try { @@ -90,5 +92,9 @@ class ScrcpyService { Map? _getEnvironment(String adbPath) => adbPath.isNotEmpty ? {'ADB': adbPath} : null; - String _getExecutable(String path) => path.isEmpty ? 'scrcpy' : path; + String _getExecutable(String path) => path.isEmpty ? getScrcpyPath() : path; + + /// Returns the resolved scrcpy binary path, preferring the Snap bundle + /// when running inside a Snap package. + String getScrcpyPath() => _snapEnvironment.getSnapScrcpyPath() ?? 'scrcpy'; } diff --git a/lib/service/snap_environment.dart b/lib/service/snap_environment.dart new file mode 100644 index 0000000..907f7f5 --- /dev/null +++ b/lib/service/snap_environment.dart @@ -0,0 +1,48 @@ +import 'dart:developer' as developer; +import 'dart:io'; + +import 'package:path/path.dart' as path; +import 'package:path_provider/path_provider.dart'; + +/// Provides information about the Snap environment the app may be running in. +class SnapEnvironment { + /// Returns `true` if the application is running inside a Snap package. + bool get isRunningInSnap => Platform.environment['SNAP'] != null; + + /// Returns the correct persistent storage directory for the app, + /// respecting the Snap environment variables when running as a Snap. + /// + /// Priority: `SNAP_USER_COMMON` → `SNAP_USER_DATA` → system app support dir. + Future getStoragePath() async { + final snapUserCommon = Platform.environment['SNAP_USER_COMMON']; + final snapUserData = Platform.environment['SNAP_USER_DATA']; + + if (snapUserCommon != null) { + return path.join(snapUserCommon, 'scrcpy_buddy'); + } else if (snapUserData != null) { + return path.join(snapUserData, 'scrcpy_buddy'); + } else { + final dir = await getApplicationSupportDirectory(); + return path.join(dir.path, 'scrcpy_buddy_db'); + } + } + + /// Returns the scrcpy binary path for the Snap bundle, or `null` if the + /// bundle path cannot be determined or the binary does not exist there. + String? getSnapScrcpyPath() { + final snapPath = Platform.environment['SNAP']; + if (snapPath == null) return null; + + developer.log('SNAP_PATH: $snapPath', name: 'scrcpy_buddy.snap'); + final scrcpyPath = '$snapPath/scrcpy-runtime/usr/local/bin/scrcpy'; + return File(scrcpyPath).existsSync() ? scrcpyPath : null; + } + + /// Returns the path to the tray icon in the Snap bundle, or `null` if the + /// bundle path cannot be determined. + String? getSnapTrayIconPath(String iconName) { + final snapDir = Platform.environment['SNAP']; + if (snapDir == null) return null; + return '$snapDir/data/flutter_assets/assets/tray/$iconName.png'; + } +} diff --git a/scripts/rebuild_snap.sh b/scripts/rebuild_snap.sh new file mode 100755 index 0000000..f7fc16c --- /dev/null +++ b/scripts/rebuild_snap.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -euo pipefail + +SNAP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SNAP_NAME="scrcpy-buddy" + +echo "==> Working directory: $SNAP_DIR" +cd "$SNAP_DIR" + +# 1. Remove installed snaps +echo "" +echo "==> [1/5] Removing installed snaps..." +sudo snap remove --purge "$SNAP_NAME" || true +sudo snap remove --purge scrcpy-runtime-2404 || true + +# 2. Clean snapcraft build artifacts +echo "" +echo "==> [2/5] Cleaning snapcraft build..." +snapcraft clean --use-lxd + +# 3. Build the snap +echo "" +echo "==> [3/5] Building snap with LXD..." +snapcraft pack --use-lxd + +# 4. Install the freshly built snap +SNAP_FILE=$(ls -t "${SNAP_DIR}"/*.snap | head -1) +echo "" +echo "==> [4/5] Installing snap: $SNAP_FILE" +sudo snap install --dangerous "$SNAP_FILE" + +# 5. Connect interfaces +echo "" +echo "==> [5/5] Connecting snap interfaces..." +sudo snap disconnect "$SNAP_NAME:gnome-46-2404" || true +sudo snap connect "$SNAP_NAME:gnome-46-2404" gnome-46-2404:gnome-46-2404 +sudo snap connect "$SNAP_NAME:scrcpy-runtime-2404" scrcpy-runtime-2404:scrcpy-runtime-2404 + +echo "" +echo "==> Done! You can now launch: snap run $SNAP_NAME" diff --git a/snap/gui/scrcpy-buddy.desktop b/snap/gui/scrcpy-buddy.desktop index cda28ba..6a34b3a 100644 --- a/snap/gui/scrcpy-buddy.desktop +++ b/snap/gui/scrcpy-buddy.desktop @@ -2,7 +2,7 @@ Name=scrcpy buddy Comment=GUI wrapper for scrcpy Exec=scrcpy-buddy -Icon=${SNAP}/meta/gui/icon_light.png +Icon=${SNAP}/meta/gui/icon.png Terminal=false Type=Application Categories=Utility; diff --git a/snap/snapcraft-strict.yaml b/snap/snapcraft-strict.yaml deleted file mode 100644 index f2c9349..0000000 --- a/snap/snapcraft-strict.yaml +++ /dev/null @@ -1,116 +0,0 @@ -# This one doesn't work x_x - -name: scrcpy-buddy -version: '1.0.0' -summary: GUI wrapper for scrcpy -description: | - A simple, clean, minimalist GUI wrapper for scrcpy (https://github.com/Genymobile/scrcpy). -grade: stable -base: core24 -confinement: strict - -#plugs: -# system-binaries: -# interface: system-files -# read: -# - /usr/bin/adb -# - /usr/bin/scrcpy - -apps: - scrcpy-buddy: - command: scrcpy_buddy - extensions: [gnome] - environment: - PATH: $SNAP/lib:$SNAP/usr/lib:$SNAP/bin:$SNAP/usr/bin:$SNAP/usr/local/bin:$PATH - LD_LIBRARY_PATH: "$LD_LIBRARY_PATH:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/android:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/pulseaudio:/var/lib/snapd/lib/gl" - #SCRCPY_SERVER_PATH: "$SNAP/usr/local/share/scrcpy/scrcpy-server" - # Point to bundled binaries - SCRCPY_BUDDY_ADB: "$SNAP/command/adb.wrapper" - #SCRCPY_BUDDY_SCRCPY: "$SNAP/usr/local/bin/scrcpy" - plugs: - - adb-support - - desktop - - desktop-legacy - - x11 - - unity7 - - wayland - - opengl - - network - - network-bind - - home - - process-control - - hardware-observe - slots: - - dbus-scrcpy-buddy - - # Expose scrcpy command directly - #scrcpy: - # command: usr/local/bin/scrcpy - # environment: - # LD_LIBRARY_PATH: "$LD_LIBRARY_PATH:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/android:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/pulseaudio:/var/lib/snapd/lib/gl" - # MESA_GLSL_CACHE_DIR: "$SNAP_USER_DATA" - # LIBGL_DRIVERS_PATH: "$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/dri" - # SCRCPY_SERVER_PATH: "$SNAP/usr/local/share/scrcpy/scrcpy-server" - # plugs: - # - adb-support - # - desktop - # - x11 - # - wayland - # - opengl - # - network - - # Expose adb command directly - adb: - command: command/adb.wrapper - environment: - LD_LIBRARY_PATH: "$LD_LIBRARY_PATH:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/android/" - plugs: - - adb-support - - network - -slots: - dbus-scrcpy-buddy: - interface: dbus - bus: session - name: dev.codertainment.scrcpy_buddy - -parts: - scrcpy-buddy: - source: . - plugin: flutter - flutter-target: lib/main.dart - override-build: | - craftctl default - # Create adb wrapper - mkdir -p $CRAFT_PART_INSTALL/command - cat > $CRAFT_PART_INSTALL/command/adb.wrapper << 'EOF' - #!/bin/bash - export LD_LIBRARY_PATH="$SNAP/usr/lib/x86_64-linux-gnu/android:$LD_LIBRARY_PATH" - exec "$SNAP/usr/bin/adb" "$@" - EOF - chmod +x $CRAFT_PART_INSTALL/command/adb.wrapper -# override-prime: | -# # Clean any existing CMake cache -# rm -rf $CRAFT_PART_BUILD/build/linux/x64/release/CMakeCache.txt -# rm -rf $CRAFT_PART_BUILD/build/linux/x64/release/CMakeFiles -# -# craftctl default - stage-packages: - - adb - - android-sdk-platform-tools-common - - libusb-1.0-0 - - scrcpy - organize: - assets/icon_light_256.png: meta/gui/icon_light.png - - #scrcpy: - # plugin: nil - # stage-snaps: - # - scrcpy - # stage: - # # Exclude conflicting libraries - # - -lib/x86_64-linux-gnu/libcom_err.so.2.1 - # - -usr/lib/x86_64-linux-gnu/libsensors.so.4.4.0 - # - -usr/lib/x86_64-linux-gnu/libtiff.so.5.* - # - -usr/lib/x86_64-linux-gnu/libxml2.so.2.* - # - -usr/share/doc/*/changelog.Debian.gz diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index f34c6c3..77fdb0a 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,20 +1,93 @@ name: scrcpy-buddy -version: '1.0.5' -summary: GUI wrapper for scrcpy -description: | - A simple, clean, minimalist GUI wrapper for scrcpy (https://github.com/Genymobile/scrcpy). +version: "1.0.5" +title: scrcpy buddy grade: stable base: core24 -confinement: classic +confinement: strict +summary: GUI for scrcpy +description: | + A simple, clean, minimalist GUI for scrcpy (https://github.com/Genymobile/scrcpy). +license: Apache-2.0 +contact: https://github.com/Codertainment/scrcpy_buddy/issues +issues: https://github.com/Codertainment/scrcpy_buddy/issues +source-code: https://github.com/Codertainment/scrcpy_buddy +website: https://github.com/Codertainment/scrcpy_buddy +donation: https://github.com/Codertainment/scrcpy_buddy + +# Suppress lint warnings for libraries that are either dynamically loaded +# at runtime (libobjectbox.so) or pulled in as transitive dependencies. +lint: + ignore: + - library: + - lib/libobjectbox.so + - usr/lib/*/libcolordprivate.so.2* + - usr/lib/*/libdconf.so.1* + - usr/lib/*/libicuio.so.74* + - usr/lib/*/libicutest.so.74* + - usr/lib/*/libpsx.so.2* + - usr/lib/*/libicutu.so.74* + - usr/lib/*/libicui18n.so.74* + +plugs: + ffmpeg-2404: + interface: content + target: $SNAP/ffmpeg-platform + default-provider: ffmpeg-2404 + + scrcpy-runtime-2404: + interface: content + target: $SNAP/scrcpy-runtime + default-provider: scrcpy-runtime-2404 apps: + adb: + command: usr/bin/adb + environment: + LD_LIBRARY_PATH: ${SNAP}/usr/lib/${CRAFT_ARCH_TRIPLET_BUILD_FOR}/android:${LD_LIBRARY_PATH} + plugs: + - adb-support + - network + - network-bind + - raw-usb + scrcpy-buddy: command: scrcpy_buddy + extensions: [ gnome ] environment: - PATH: /usr/local/bin:/usr/bin:/bin:$PATH - # GTK/GNOME settings for Flutter - GTK_USE_PORTAL: 1 - XDG_DATA_DIRS: $SNAP/usr/share:$XDG_DATA_DIRS + LD_LIBRARY_PATH: ${SNAP}/scrcpy-runtime/usr/lib/${CRAFT_ARCH_TRIPLET_BUILD_FOR}:${SNAP}/usr/lib/${CRAFT_ARCH_TRIPLET_BUILD_FOR}/android:${LD_LIBRARY_PATH}:${SNAP}/ffmpeg-platform/usr/lib/${CRAFT_ARCH_TRIPLET_BUILD_FOR} + PATH: ${SNAP}/scrcpy-runtime/usr/local/bin:${PATH} + + plugs: + # For running the Android Debug Bridge (adb) and + # connecting the Android devices over USB or TCP/IP. + # https://github.com/Genymobile/scrcpy/blob/master/doc/connection.md + - adb-support + - network + - network-bind + - raw-usb + + # For scrcpy audio forwarding functionality. + # https://github.com/Genymobile/scrcpy/blob/master/doc/audio.md + - alsa + - audio-playback + - pulseaudio + + # For scrcpy v4l2 emulation support. + # https://github.com/Genymobile/scrcpy/blob/master/doc/v4l2.md + - camera + + # For scrcpy video & audio recording support. + # https://github.com/Genymobile/scrcpy/blob/master/doc/recording.md + - home + - removable-media + + # For scrcpy gamepad simulation support. + # https://github.com/Genymobile/scrcpy/blob/master/doc/gamepad.md + - joystick + + # Legacy desktop support. + # https://github.com/canonical/snapd/blob/master/interfaces/builtin/unity7.go + - unity7 slots: - dbus-scrcpy-buddy @@ -24,27 +97,87 @@ slots: bus: session name: dev.codertainment.scrcpy_buddy +layout: + # WORKAROUND: + # libproxy.so.1 (from gnome-46-2404) has a hardcoded RUNPATH of + # /usr/lib/${CRAFT_ARCH_TRIPLET_BUILD_FOR}/libproxy for loading + # libpxbackend-1.0.so. Map it so the dynamic linker can resolve it. + # https://github.com/canonical/snapcraft/issues/6054 + /usr/lib/${CRAFT_ARCH_TRIPLET_BUILD_FOR}/libproxy: + symlink: $SNAP/gnome-platform/usr/lib/${CRAFT_ARCH_TRIPLET_BUILD_FOR}/libproxy + # This fixes the "ERROR: Could not open icon image: /usr/local/share/icons/hicolor/256x256/apps/scrcpy.png" + # error at runtime. + /usr/local/share/icons: + symlink: $SNAP/scrcpy-runtime/usr/local/share/icons + # This allows scrcpy to locate the scrcpy-server binary. + # "ERROR: '/usr/local/share/scrcpy/scrcpy-server' does not exist or is not a regular file" + /usr/local/share/scrcpy: + symlink: $SNAP/scrcpy-runtime/usr/local/share/scrcpy + /usr/share/alsa: + bind: $SNAP/usr/share/alsa + parts: + adb: + plugin: nil + stage-packages: + - adb + - android-libbase + - android-libboringssl + - android-libcutils + - android-liblog + - android-libziparchive + - libbrotli1 + - libcap2 + - liblz4-1 + - libprotobuf32t64 + - libudev1 + - libusb-1.0-0 + - libzstd1 + + lp-bug-2016358-workaround: + plugin: nil + override-prime: | + craftctl default + mkdir -p "${CRAFT_PRIME}/"{ffmpeg-2404,scrcpy-runtime} + + # Avoid buggy mount namespace handling in snapd, which can prevent + # manually connected content interfaces from appearing in the snap + # runtime. + # https://bugs.launchpad.net/snapd/+bug/2144666 + lp-bug-2144666-workaround: + plugin: nil + override-prime: | + set -eu + craftctl default + + content_interface_mount_target_dirs=( + # From the "gnome" extension + "${CRAFT_PRIME}/gpu-2404" + "${CRAFT_PRIME}/gpu-2404-2" # mesa-2404 content in spool mode + "${CRAFT_PRIME}/data-dir/icons" + "${CRAFT_PRIME}/data-dir/sounds" + "${CRAFT_PRIME}/data-dir/themes" + "${CRAFT_PRIME}/gnome-platform" + + # From the "scrcpy-runtime-2404" content interface + "${CRAFT_PRIME}/scrcpy-runtime" + + # From the "ffmpeg-2404" content interface + "${CRAFT_PRIME}/ffmpeg-platform" + ) + for dir in "${content_interface_mount_target_dirs[@]}"; do + mkdir -p "${dir}" + done + scrcpy-buddy: source: . plugin: flutter flutter-target: lib/main.dart build-packages: - - pkg-config - - libgtk-3-dev - - libglib2.0-dev - - libgdk-pixbuf2.0-dev - - libcairo2-dev - - libpango1.0-dev - - libayatana-appindicator3-dev # For tray_manager + - libayatana-appindicator3-dev # For tray_manager stage-packages: - # Add GTK dependencies since we removed gnome extension - - libgtk-3-0 - - libgdk-pixbuf2.0-0 - - libglib2.0-0 - - libcairo2 - - libpango-1.0-0 - - libatk1.0-0 - - libayatana-appindicator3-1 # For tray_manager + - libayatana-appindicator3-1 # For tray_manager + - libdbusmenu-gtk3-4 # Missing transitive dep + - libayatana-ido3-0.4-0 # Missing transitive dep organize: - assets/icon_light_256.png: meta/gui/icon_light.png + data/flutter_assets/assets/icon_light_256.png: meta/gui/icon.png