diff --git a/.github/workflows/flutter_analysis.yml b/.github/workflows/flutter_analysis.yml index 27f7d1dc..dc144a1a 100644 --- a/.github/workflows/flutter_analysis.yml +++ b/.github/workflows/flutter_analysis.yml @@ -18,8 +18,9 @@ jobs: - name: Install Flutter uses: subosito/flutter-action@v2.8.0 with: - channel: master + channel: beta + - run: flutter upgrade - run: flutter pub get - run: flutter gen-l10n diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e1bc35a6..6616809a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,7 +17,7 @@ jobs: submodules: recursive - uses: subosito/flutter-action@v2.8.0 with: - channel: "master" + channel: "beta" # cache: true # TODO: Signing Android application. # - name: Create Key Store @@ -67,7 +67,7 @@ jobs: submodules: recursive - uses: subosito/flutter-action@v2.8.0 with: - channel: "master" + channel: "beta" architecture: x64 # cache: true @@ -87,7 +87,7 @@ jobs: # body: "" # tag_name: "bleeding_edge" # files: | - # bluecherry-dvr-setup.exe + # .exe # token: ${{ secrets.GITHUB_TOKEN }} build_windows: @@ -101,7 +101,7 @@ jobs: submodules: recursive - uses: subosito/flutter-action@v2.8.0 with: - channel: "master" + channel: "beta" # cache: true - run: git config --system core.longpaths true - run: flutter gen-l10n @@ -113,7 +113,7 @@ jobs: "%programfiles(x86)%\Inno Setup 6\iscc.exe" "installer/windows-installer.iss" shell: cmd - - run: cp installer\Output\bluecherry-dvr-setup.exe bluecherry-dvr-setup.exe + - run: cp installer\Output\bluecherry-windows-setup.exe bluecherry-windows-setup.exe - name: Release # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-using-contexts @@ -125,7 +125,7 @@ jobs: body: "" tag_name: "bleeding_edge" files: | - bluecherry-dvr-setup.exe + bluecherry-windows-setup.exe token: ${{ secrets.GITHUB_TOKEN }} build_linux: @@ -146,26 +146,24 @@ jobs: - name: Install Flutter uses: subosito/flutter-action@v2.8.0 with: - # Stable channel is necessary because playback is broken on master - channel: "stable" + channel: "beta" # cache: true - - name: Build Flutter + + - name: Initiate Flutter run: | flutter gen-l10n flutter pub get - flutter build linux --verbose - - name: Build AppImage + - name: Build Flutter for RPM run: | - sudo pip3 install git+https://github.com/AppImageCrafters/appimage-builder.git - appimage-builder --skip-tests - cp Bluecherry-latest-x86_64.AppImage bluecherry-linux-x86_64.AppImage + flutter build linux --verbose --dart-define-from-file=linux/env/rpm.json - name: Build RPM Package run: | mkdir -p linux/debian/usr/bin cp -fr build/linux/x64/release/bundle linux/debian/usr/share/bluecherry_client ln -sr linux/debian/usr/share/bluecherry_client/bluecherry_client linux/debian/usr/bin/bluecherry_client + sed -i "s:cp -rf :cp -rf $(pwd)/:" linux/rpm/bluecherry.spec cd linux/debian sed -i "s:FILES_HERE:$(find usr \( -type l -o -type f \) -follow -print | awk '{printf "/%s\\n", $0}'):" ../rpm/bluecherry.spec @@ -173,11 +171,35 @@ jobs: rpmbuild -bb linux/rpm/bluecherry.spec -D "_topdir $(pwd)/rpmbuild" cp rpmbuild/RPMS/x86_64/*.rpm bluecherry-linux-x86_64.rpm + - name: Build Flutter for DEB + run: | + flutter clean + flutter gen-l10n + flutter pub get + flutter build linux --verbose --dart-define-from-file=linux/env/deb.json + + rm -r linux/debian/usr/bin + mkdir -p linux/debian/usr/bin + cp -fr build/linux/x64/release/bundle linux/debian/usr/share/bluecherry_client + ln -sr linux/debian/usr/share/bluecherry_client/bluecherry_client linux/debian/usr/bin/bluecherry_client + - name: Build DEB Package run: | dpkg-deb --build --root-owner-group linux/debian cp linux/*.deb bluecherry-linux-x86_64.deb + - name: Build Flutter for Tarball + run: | + flutter clean + flutter gen-l10n + flutter pub get + flutter build linux --verbose --dart-define-from-file=linux/env/tar.gz.json + + rm -r linux/debian/usr/bin + mkdir -p linux/debian/usr/bin + cp -fr build/linux/x64/release/bundle linux/debian/usr/share/bluecherry_client + ln -sr linux/debian/usr/share/bluecherry_client/bluecherry_client linux/debian/usr/bin/bluecherry_client + - name: Build Tarball run: | mkdir -p AppDir/ @@ -185,6 +207,22 @@ jobs: ln -sr AppDir/usr/bin/bluecherry_client AppDir/bluecherry_client tar czf bluecherry-linux-x86_64.tar.gz -C AppDir/ . + - name: Build Flutter for AppImage + run: | + rm -r AppDir/ + mkdir -p AppDir/ + + flutter clean + flutter gen-l10n + flutter pub get + flutter build linux --verbose --dart-define-from-file=linux/env/appimage.json + + - name: Build AppImage + run: | + sudo pip3 install git+https://github.com/AppImageCrafters/appimage-builder.git + appimage-builder --skip-tests + cp Bluecherry-latest-x86_64.AppImage bluecherry-linux-x86_64.AppImage + - name: Release # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-using-contexts if: ${{ github.event_name == 'push' }} diff --git a/.gitignore b/.gitignore index 92150d30..d6215449 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ .pub-cache/ .pub/ /build/ +.fvm/ # Web related lib/generated_plugin_registrant.dart diff --git a/AppImageBuilder.yml b/AppImageBuilder.yml index 68ffbecb..42d2cca2 100644 --- a/AppImageBuilder.yml +++ b/AppImageBuilder.yml @@ -60,7 +60,6 @@ AppDir: include: - assets/images/background.webp - assets/images/icon.png - - version.txt exclude: - usr/share/man - usr/share/doc/*/README.* diff --git a/README.md b/README.md index d7e5af80..80af8878 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ This project & work under this repository is licensed under [GNU General Public | Android | iOS | Windows | GNU/Linux | MacOS | | ------- | --- | ------- | ----- | ----- | -| [arm64 `.apk`](https://github.com/bluecherrydvr/unity/releases/download/bleeding_edge/bluecherry-android-arm64-v8a-release.apk) | [App Store](https://apps.apple.com/us/app/bluecherry-mobile/id1555805139) | [Windows Setup](https://github.com/bluecherrydvr/unity/releases/download/v3.0.0-beta8/bluecherry-dvr-setup.exe) | [AppImage](https://github.com/bluecherrydvr/unity/releases/download/bleeding_edge/Bluecherry-latest.AppImage) | 🚧 **SOON** ~~[App Store](https://github.com/bluecherrydvr/unity/issues/112)~~ | +| [arm64 `.apk`](https://github.com/bluecherrydvr/unity/releases/download/bleeding_edge/bluecherry-android-arm64-v8a-release.apk) | [App Store](https://apps.apple.com/us/app/bluecherry-mobile/id1555805139) | [Windows Setup](https://github.com/bluecherrydvr/unity/releases/download/v3.0.0-beta8/bluecherry-windows-setup.exe) | [AppImage](https://github.com/bluecherrydvr/unity/releases/download/bleeding_edge/Bluecherry-latest.AppImage) | 🚧 **SOON** ~~[App Store](https://github.com/bluecherrydvr/unity/issues/112)~~ | | [armabi `.apk`](https://github.com/bluecherrydvr/unity/releases/download/bleeding_edge/bluecherry-android-armeabi-v7a-release.apk) | | 🚧 **SOON** ~~`winget install bluecherry`~~ | [Ubuntu/Debian `.deb`](https://github.com/bluecherrydvr/unity/releases/download/bleeding_edge/bluecherry-linux-x86_64.deb) | | | [x86_64 `.apk`](https://github.com/bluecherrydvr/unity/releases/download/bleeding_edge/bluecherry-android-x86_64-release.apk) | | 🚧 **SOON** ~~Microsoft Store~~ | [Raw Executable `.tar.gz`](https://github.com/bluecherrydvr/unity/releases/download/bleeding_edge/bluecherry-linux-x86_64.tar.gz) | | | 🚧 **SOON** ~~Play Store~~ | | | [Fedora/Red Hat Linux `.rpm`](https://github.com/bluecherrydvr/unity/releases/download/bleeding_edge/bluecherry-linux-x86_64.rpm) | | @@ -87,7 +87,7 @@ Let's say, we're adding French (`fr`) translation. Send us details about any issues you discover [in the issues](https://github.com/bluecherrydvr/unity/issues) or [in the forums](https://forums.bluecherrydvr.com/). -## Contribute +## Contribute & Technical Review The code uses [Provider](https://github.com/rrousselGit/provider) for state-management because it is widely known by Flutter community, doesn't bring any unnecessary complexity to the codebase & is scalable/stable enough. @@ -120,6 +120,7 @@ lib │ ├───mobile_view_provider.dart [stores, provides & caches mobile camera layout etc.] │ ├───server_provider.dart [stores, provides & caches multiple DVR servers added by the user.] │ └───settings_provider.dart [stores, provides & caches various in-app configurations & settings.] +│ └───update_provider.dart [manages app updates and app status.] │ ├───utils [constant values, helper functions & theme-related stuff.] │ ├───constants.dart @@ -139,3 +140,23 @@ lib ``` Feel free to send any pull-requests to add any features you wish or fix any bugs you notice. + +### Build + +The build process is pretty straight-forward. You need to have [Flutter](https://flutter.dev/docs/get-started/install) installed on your system. + +```bash +git clone https://github.com/bluecherrydvr/unity +cd unity +flutter pub get +flutter gen-l10n +flutter build [linux|windows|android|ios] +``` + +The automated build process is done using GitHub Actions. You may find the workflow [here](.github/workflows/main.yml). The workflow builds the app for all supported platforms & uploads the artifacts to the release page. + +On Linux, a Flutter executable with different environment variables is used to build the app for different distributions. This tells the app how the system is configured and how it should install updates. To run for Linux, you need to provide the following environment variables based on your system, where `[DISTRO_ENV]` can be `appimage`, `deb`, `rpm` or `tar.gz` (Tarball). + +```bash +flutter run --dart-define-from-file=linux/env/[DISTRO_ENV].json +``` diff --git a/bluecherry_appcast.xml b/bluecherry_appcast.xml new file mode 100644 index 00000000..532a36c7 --- /dev/null +++ b/bluecherry_appcast.xml @@ -0,0 +1,35 @@ + + + + Bluecherry - Appcast + + Version 3.0.0-beta8 + Linux support and a bunch of bug fixes and performance improvements. + Thu, 29 Jun 2023 + + + + Version 3.0.0-beta7 + PTZ cameras support, reworked grid on mobile and bug fixes. + Fri, 26 May 2023 + + + + Version 3.0.0-beta6 + Focused on bug fixes + Thu, 16 Mar 2023 + + + + Version 3.0.0-beta5 + Events Timeline + Thu, 2 Mar 2023 + + + + Version 3.0.0-beta4 + Playback updates. Keyboard support + Thu, 2 Feb 2023 + + + \ No newline at end of file diff --git a/installer/windows-installer.iss b/installer/windows-installer.iss index b732ed10..7198af12 100644 --- a/installer/windows-installer.iss +++ b/installer/windows-installer.iss @@ -1,9 +1,8 @@ ; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! -#include "../version.txt" - #define MyAppName "Bluecherry DVR" +#define MyAppVersion "3.0.0-beta8" #define MyAppPublisher "Bluecherry DVR" #define MyAppURL "https://www.bluecherrydvr.com/" #define MyAppExeName "bluecherry_client.exe" @@ -24,7 +23,7 @@ DisableProgramGroupPage=yes LicenseFile=..\LICENSE ; Uncomment the following line to run in non administrative install mode (install for current user only.) ;PrivilegesRequired=lowest -OutputBaseFilename=bluecherry-dvr-setup +OutputBaseFilename=bluecherry-windows-setup SetupIconFile=..\assets\images\icon.ico Compression=lzma SolidCompression=yes @@ -73,3 +72,5 @@ Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: de [Run] Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent +[UninstallDelete] +Type: filesandordirs; Name: "{userappdata}\com.bluecherry" \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 9462c0e6..d2d2ab2e 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -183,6 +183,9 @@ "filter": "Filter", "fromDate": "From", "toDate": "To", + "today": "Today", + "yesterday": "Yesterday", + "never": "never", "allowAlarms": "Allow alarms", "nextEvents": "Next events", "nEvents": "{n} events", @@ -242,5 +245,31 @@ "p720": "720p", "p480": "480p", "p360": "360p", - "p240": "240p" + "p240": "240p", + "@updates": {}, + "updates": "Updates", + "upToDate": "You are up to date.", + "lastChecked": "Last checked: {date}", + "@lastChecked": { + "placeholders": { + "date": {} + } + }, + "checkForUpdates": "Check for updates", + "checkingForUpdates": "Checking for updates", + "automaticDownloadUpdates": "Automatic download updates", + "automaticDownloadUpdatesDescription": "Be among the first to get the latest updates, fixes and improvements as they roll out.", + "updateHistory": "Update history", + "newVersionAvailable": "New version available", + "installVersion": "Install", + "downloadVersion": "Download", + "learnMore": "Learn more", + "failedToUpdate": "Failed to update", + "executableNotFound": "Executable not found", + "runningOn": "Running on {platform}", + "@runningOn": { + "placeholders": { + "platform": {} + } + } } diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index d3f0dea3..de1768b3 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -177,6 +177,9 @@ "filter": "Filtrer", "fromDate": "De", "toDate": "À", + "today": "Today", + "yesterday": "Yesterday", + "never": "never", "allowAlarms": "Permettre les alarmes", "@Event Priorities": {}, "info": "Information", @@ -210,5 +213,41 @@ "moveWest": "Move west", "moveEast": "Move east", "moveWide": "Zoom out", - "moveTele": "Zoom in" + "moveTele": "Zoom in", + "presets": "Presets", + "noPresets": "No presets found", + "newPreset": "New preset", + "goToPreset": "Go to preset", + "renamePreset": "Rename preset", + "deletePreset": "Delete preset", + "refreshPresets": "Refresh presets", + "@Resolution": {}, + "selectResolution": "Select resolution", + "setResolution": "Set resolution", + "setResolutionDescription": "The resolution of the video stream can highly impact the performance of the app. Set the resolution to a lower value to improve performance, or to a higher value to improve quality. You can set the default resolution to every camera in the settings", + "hd": "High definition", + "defaultResolution": "Default resolution", + "p1080": "1080p", + "p720": "720p", + "p480": "480p", + "p360": "360p", + "p240": "240p", + "@updates": {}, + "updates": "Updates", + "upToDate": "You are up to date.", + "lastChecked": "Last checked: {date}", + "@lastChecked": { + "placeholders": { + "date": {} + } + }, + "checkForUpdates": "Check for updates", + "checkingForUpdates": "Checking for updates", + "automaticDownloadUpdates": "Automatic download updates", + "automaticDownloadUpdatesDescription": "Be among the first to get the latest updates, fixes and improvements as they roll out.", + "updateHistory": "Update history", + "newVersionAvailable": "New version available", + "installVersion": "Install", + "downloadVersion": "Download", + "learnMore": "Learn more" } diff --git a/lib/main.dart b/lib/main.dart index 35ecb2c1..18cf9ab2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -32,6 +32,7 @@ import 'package:bluecherry_client/providers/home_provider.dart'; import 'package:bluecherry_client/providers/mobile_view_provider.dart'; import 'package:bluecherry_client/providers/server_provider.dart'; import 'package:bluecherry_client/providers/settings_provider.dart'; +import 'package:bluecherry_client/providers/update_provider.dart'; import 'package:bluecherry_client/utils/storage.dart'; import 'package:bluecherry_client/utils/theme.dart'; import 'package:bluecherry_client/utils/window.dart'; @@ -55,13 +56,17 @@ import 'package:unity_video_player/unity_video_player.dart'; final navigatorKey = GlobalKey(); Future main(List args) async { + WidgetsFlutterBinding.ensureInitialized(); + // https://github.com/flutter/flutter/issues/41980#issuecomment-1231760866 // On windows, the window is hidden until flutter draws its first frame. // To create a splash screen effect while the dependencies are loading, we // can run the [SplashScreen] widget as the app. - if (isDesktop) runApp(const SplashScreen()); + if (isDesktop) { + await configureWindow(); + runApp(const SplashScreen()); + } - WidgetsFlutterBinding.ensureInitialized(); DevHttpOverrides.configureCertificates(); await UnityVideoPlayerInterface.instance.initialize(); await configureStorage(); @@ -76,8 +81,6 @@ Future main(List args) async { final windowType = MultiWindowType.values[int.tryParse(args[0]) ?? 0]; final themeMode = ThemeMode.values[int.tryParse(args[2]) ?? 0]; - configureWindow(); - switch (windowType) { case MultiWindowType.device: final device = Device.fromJson(json.decode(args[1])); @@ -128,12 +131,12 @@ Future main(List args) async { // settings provider needs to be initalized alone await SettingsProvider.ensureInitialized(); await Future.wait([ - if (isDesktop) configureWindow(), MobileViewProvider.ensureInitialized(), DesktopViewProvider.ensureInitialized(), ServersProvider.ensureInitialized(), DownloadsManager.ensureInitialized(), EventsProvider.ensureInitialized(), + UpdateManager.ensureInitialized(), ]); /// Firebase messaging isn't available on desktop platforms @@ -141,9 +144,7 @@ Future main(List args) async { FirebaseConfiguration.ensureInitialized(); } - if (!isMobile) { - HomeProvider.setDefaultStatusBarStyle(); - } + HomeProvider.setDefaultStatusBarStyle(); runApp(const UnityApp()); } @@ -174,6 +175,9 @@ class UnityApp extends StatelessWidget { ChangeNotifierProvider.value( value: EventsProvider.instance, ), + ChangeNotifierProvider.value( + value: UpdateManager.instance, + ), ], child: Consumer( builder: (context, settings, _) => MaterialApp( diff --git a/lib/providers/downloads_provider.dart b/lib/providers/downloads_provider.dart index 52f0bb78..5d804c4b 100644 --- a/lib/providers/downloads_provider.dart +++ b/lib/providers/downloads_provider.dart @@ -112,8 +112,6 @@ class DownloadsManager extends ChangeNotifier { } } - /// Saves current layout/order of [Device]s to cache using `package:hive`. - /// Pass [notifyListeners] as `false` to prevent redundant redraws. Future _save({bool notify = true}) async { await downloads.write({ kHiveDownloads: @@ -123,19 +121,17 @@ class DownloadsManager extends ChangeNotifier { if (notify) notifyListeners(); } - /// Restores current layout/order of [Device]s from `package:hive` cache. Future _restore({bool notifyListeners = true}) async { final data = await downloads.read() as Map; - downloadedEvents = ((await compute( - jsonDecode, - data[kHiveDownloads] as String, - ) ?? - []) as List) - .cast() - .map((item) { - return DownloadedEvent.fromJson(item.cast()); - }).toList(); + downloadedEvents = data[kHiveDownloads] == null + ? [] + : ((await compute(jsonDecode, data[kHiveDownloads] as String) ?? []) + as List) + .cast() + .map((item) { + return DownloadedEvent.fromJson(item.cast()); + }).toList(); if (notifyListeners) { this.notifyListeners(); @@ -178,7 +174,7 @@ class DownloadsManager extends ChangeNotifier { final dir = SettingsProvider.instance.downloadsDirectory; final fileName = 'event_${event.id}${event.deviceID}${event.server.ip}.mp4'; - final downloadPath = '$dir${path.separator}$fileName'; + final downloadPath = path.join(dir, fileName); await Dio().downloadUri( event.mediaURL!, diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index 522564fd..63d386a4 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -45,7 +45,7 @@ class SettingsProvider extends ChangeNotifier { static const kDefaultLayoutCyclingTogglePeriod = Duration(seconds: 30); static Future kDefaultDownloadsDirectory() async { final docsDir = await getApplicationSupportDirectory(); - return Directory('${docsDir.path}${path.separator}downloads').create(); + return Directory(path.join(docsDir.path, 'downloads')).create(); } // Getters. diff --git a/lib/providers/update_provider.dart b/lib/providers/update_provider.dart new file mode 100644 index 00000000..da04e512 --- /dev/null +++ b/lib/providers/update_provider.dart @@ -0,0 +1,416 @@ +/* + * This file is a part of Bluecherry Client (https://github.com/bluecherrydvr/unity). + * + * Copyright 2022 Bluecherry, LLC + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import 'dart:io'; + +import 'package:bluecherry_client/utils/constants.dart'; +import 'package:bluecherry_client/utils/storage.dart'; +import 'package:bluecherry_client/widgets/misc.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; +import 'package:http/http.dart' as http; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider/path_provider.dart'; +import 'package:unity_video_player/unity_video_player.dart'; +import 'package:version/version.dart'; +import 'package:window_manager/window_manager.dart'; +import 'package:xml/xml.dart'; + +enum FailType { + executableNotFound, +} + +class UpdateVersion { + final String version; + final String description; + final String publishedAt; + + const UpdateVersion({ + required this.version, + required this.description, + required this.publishedAt, + }); + + @override + String toString() => + 'UpdateVersion(version: $version, description: $description, publishedAt: $publishedAt)'; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is UpdateVersion && + other.version == version && + other.description == description && + other.publishedAt == publishedAt; + } + + @override + int get hashCode => + version.hashCode ^ description.hashCode ^ publishedAt.hashCode; + + static const titleField = 'title'; + static const descriptionField = 'description'; + static const publishedAtField = 'pubDate'; + + static const rpm = 'rpm'; + static const deb = 'deb'; + static const tarball = 'tar.gz'; + static const appImage = 'appimage'; + static const linuxDownloadFileName = 'bluecherry-linux-x86_64'; + static const windowsDownloadFileName = 'bluecherry-windows-setup'; +} + +class UpdateManager extends ChangeNotifier { + UpdateManager._(); + + /// `late` initialized [UpdateManager] instance. + static late final UpdateManager instance; + late final PackageInfo packageInfo; + late String tempDir; + + /// The URL to the appcast file. + static const appCastUrl = + 'https://raw.githubusercontent.com/bluecherrydvr/unity/main/bluecherry_appcast.xml'; + + /// Initializes the [UpdateManager] instance & fetches state from `async` + /// `package:hive` method-calls. Called before [runApp]. + static Future ensureInitialized() async { + instance = UpdateManager._(); + await instance.initialize(); + return instance; + } + + /// If true, there is a new update available. + /// + /// If false, the user is up to date with the latest version. + bool get hasUpdateAvailable { + if (this.latestVersion == null) return false; + + final currentVersion = Version.parse(packageInfo.version); + final latestVersion = Version.parse(this.latestVersion!.version); + + assert( + latestVersion >= currentVersion, + 'The latest version can not be older than the current version', + ); + + return currentVersion != latestVersion; + } + + List versions = []; + UpdateVersion? get latestVersion { + return versions.isEmpty ? null : versions.last; + } + + Future initialize() async { + final data = await downloads.read() as Map; + if (!data.containsKey(kHiveAutomaticUpdates)) { + await _save(); + } else { + await _restore(); + } + + tempDir = (await getTemporaryDirectory()).path; + + await Future.wait([ + checkForUpdates(), + PackageInfo.fromPlatform().then((result) { + packageInfo = result; + }) + ]); + + if (hasUpdateAvailable && automaticDownloads) { + download(latestVersion!.version); + } + } + + Future _save({bool notify = true}) async { + await downloads.write({ + kHiveAutomaticUpdates: automaticDownloads, + kHiveLastCheck: lastCheck?.toIso8601String(), + }); + + if (notify) notifyListeners(); + } + + Future _restore({bool notifyListeners = true}) async { + final data = await downloads.read() as Map; + + _automaticDownloads = data[kHiveAutomaticUpdates]; + _lastCheck = data[kHiveLastCheck] == null + ? null + : DateTime.tryParse(data[kHiveLastCheck]!); + + if (notifyListeners) this.notifyListeners(); + } + + /// If there is anything loading at the time + bool loading = false; + + /// Whether the user wants to automatically download updates + bool _automaticDownloads = false; + bool get automaticDownloads => _automaticDownloads; + set automaticDownloads(bool value) { + _automaticDownloads = value; + _save(); + } + + /// The last time the user checked for updates + DateTime? _lastCheck; + DateTime? get lastCheck => _lastCheck; + set lastCheck(DateTime? date) { + _lastCheck = date; + _save(); + } + + /// Checks how the Linux executable was installed. + /// + /// Returns `null` if the platform is not Linux or if the environment is not + /// recognized. + /// + /// The possible values are: + /// * `rpm` + /// * `deb` + /// * `AppImage` + /// * `tar.gz` (tarball) + /// + /// This means the value represent the file extension of the executable. + /// + /// Each Flutter executable is built with a different `linux_environment` + /// value, so it is possible to distinguish between them. This is useful to + /// know how to upgrade the app. + /// + /// See also: + /// + /// * [install], which uses this method to install the correct executable. + String? get linuxEnvironment { + assert( + Platform.isLinux, + 'This should never be reached on non-Linux platforms.', + ); + + if (!const bool.hasEnvironment('linux_environment')) return null; + + return const String.fromEnvironment('linux_environment'); + } + + /// Check if updates are supported on the current platform. + /// + /// On Windows, updates are always supported. + /// + /// On Linux, updates are supported if the `linux_environment` is set and it + /// is not an `AppImage`. + bool get isUpdatingSupported { + if (Platform.isWindows) return true; + if (Platform.isLinux) { + return linuxEnvironment != null && + linuxEnvironment != UpdateVersion.appImage; + } + + return false; + } + + /// Whether any executable is being downloaded at the time + bool downloading = false; + + /// The progress of the download, from 0.0 to 1.0 + double downloadProgress = 0.0; + + /// Gets the executable for the given [version]. + /// + /// If the executable is not found, returns `null`. To download the executable, + /// call [download(version)]. + File? executableFor(String version) { + assert(isDesktop, 'This should never be reached on non-desktop platforms'); + + if (Platform.isWindows) { + final file = File(path.join( + tempDir, + '${UpdateVersion.windowsDownloadFileName}-$version.exe', + )); + if (file.existsSync()) return file; + } else if (Platform.isLinux) { + final file = File(path.join( + tempDir, + '${UpdateVersion.linuxDownloadFileName}-$version.$linuxEnvironment', + )); + if (file.existsSync()) return file; + } else { + throw UnsupportedError( + 'Unsupported platform. Only Windows and Linux are supported', + ); + } + return null; + } + + /// Downloads the latest version executable. + Future download(String version) async { + assert(isDesktop, 'This should never be reached on non-desktop platforms'); + + downloading = true; + notifyListeners(); + + String fileName; + String extension; + + if (Platform.isWindows) { + fileName = UpdateVersion.windowsDownloadFileName; + extension = '.exe'; + } else if (Platform.isLinux) { + assert(linuxEnvironment != null); + fileName = UpdateVersion.linuxDownloadFileName; + extension = '.$linuxEnvironment'; + } else { + downloading = false; + notifyListeners(); + throw UnsupportedError( + 'Unsupported platform: ${Platform.operatingSystem}', + ); + } + + final file = File(path.join(tempDir, '$fileName-$version$extension')); + if (await file.exists()) await file.delete(); + + final executablePath = Uri.https( + 'github.com', + '/bluecherrydvr/unity/releases/download/bleeding_edge/$fileName$extension', + ); + + await Dio().downloadUri( + executablePath, + file.path, + onReceiveProgress: (received, total) { + downloadProgress = received / total * 100; + notifyListeners(); + }, + ); + + downloading = false; + downloadProgress = 0.0; + notifyListeners(); + } + + /// Installs the executable for the latest version. + /// + /// It can not downgrade + Future install({ + required ValueChanged onFail, + }) async { + assert( + isUpdatingSupported, + 'This should never be reached on unsupported platforms', + ); + + assert(hasUpdateAvailable, 'Already up to date'); + + final executable = executableFor(latestVersion!.version); + assert(executable != null, 'Executable not found'); + + if (executable == null) { + onFail(FailType.executableNotFound); + return; + } + + windowManager.hide(); + await UnityVideoPlayerInterface.dispose(); + + if (Platform.isWindows) { + // https://jrsoftware.org/ishelp/index.php?topic=technotes + Process.run(executable.path, [ + '/SP-', + '/silent', + '/noicons', + ]); + } else if (Platform.isLinux) { + switch (linuxEnvironment) { + case UpdateVersion.rpm: + Process.run('tar', ['-U', executable.path]); + break; + case UpdateVersion.deb: + Process.run('sudo', ['dpkg', '-i', executable.path]); + break; + case UpdateVersion.tarball: // tarball + Process.run('tar', ['-i', executable.path]); + break; + case UpdateVersion.appImage: + throw UnsupportedError('AppImages do not support updating from app'); + default: + throw UnsupportedError( + 'Can not install an executable on an unknown environment', + ); + } + } + + windowManager.close(); + } + + /// Check for new updates. + Future checkForUpdates() async { + loading = true; + notifyListeners(); + + final response = await http.get(Uri.parse(appCastUrl)); + + if (response.statusCode != 200) { + debugPrint( + 'Failed to check for updates (${response.statusCode}): ${response.body}', + ); + loading = false; + notifyListeners(); + return; + } + + final versions = []; + final doc = XmlDocument.parse(response.body); + for (final item in doc.findAllElements('item')) { + late String version; + late String description; + late String publishedAt; + for (var child in item.children.whereType()) { + switch (child.name.toString()) { + case UpdateVersion.titleField: + version = child.innerText.replaceAll('Version', '').trim(); + break; + case UpdateVersion.descriptionField: + description = child.innerText.trim(); + break; + case UpdateVersion.publishedAtField: + publishedAt = child.innerText.trim(); + break; + default: + } + } + versions.add(UpdateVersion( + version: version, + description: description, + publishedAt: publishedAt, + )); + } + versions.sort( + (a, b) => Version.parse(a.version).compareTo(Version.parse(b.version)), + ); + + if (versions != this.versions) this.versions = versions; + + loading = false; + lastCheck = DateTime.now(); // this updates the screen already + } +} diff --git a/lib/utils/constants.dart b/lib/utils/constants.dart index d52bf947..6dbf550b 100644 --- a/lib/utils/constants.dart +++ b/lib/utils/constants.dart @@ -49,6 +49,8 @@ const kHiveDownloads = 'downloads'; const kHiveEventsPlayback = 'events_playback'; const kHiveLayoutCycling = 'layout_cycling'; const kHiveLayoutCyclingPeriod = 'layout_cycling_period'; +const kHiveAutomaticUpdates = 'automatic_download_updates'; +const kHiveLastCheck = 'last_update_check'; /// Used as frame buffer size in [DeviceTile], and calculating aspect ratio. Only relevant on desktop. const kDeviceTileWidth = 640.0; diff --git a/lib/utils/methods.dart b/lib/utils/methods.dart index 199252da..a8fe3e18 100644 --- a/lib/utils/methods.dart +++ b/lib/utils/methods.dart @@ -81,19 +81,3 @@ T? showIf(bool condition, {required T child}) { return null; } - -/// The current app version -/// -/// To update it, update it in the windows installer -Future get appVersion async { - final installer = await rootBundle.loadString( - 'version.txt', - ); - - return installer - .split('\n') - .firstWhere((line) => line.startsWith('#define MyAppVersion')) - .replaceAll('#define MyAppVersion', '') - .replaceAll('"', '') - .trim(); -} diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 412bd4b8..21b23da9 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -36,6 +36,7 @@ Future configureStorage() async { serversStorage = SafeLocalStorage(path.join(dir, 'servers.json')); mobileView = SafeLocalStorage(path.join(dir, 'mobileView.json')); desktopView = SafeLocalStorage(path.join(dir, 'desktopView.json')); + updates = SafeLocalStorage(path.join(dir, 'updates.json')); // Migrate from hive to new storage system @@ -90,6 +91,7 @@ late final SafeLocalStorage serversStorage; late final SafeLocalStorage settings; late final SafeLocalStorage mobileView; late final SafeLocalStorage desktopView; +late final SafeLocalStorage updates; extension SafeLocalStorageExtension on SafeLocalStorage { Future add(Map data) async { diff --git a/lib/widgets/add_server_wizard.dart b/lib/widgets/add_server_wizard.dart index a93f2554..f4cdfcb7 100644 --- a/lib/widgets/add_server_wizard.dart +++ b/lib/widgets/add_server_wizard.dart @@ -292,6 +292,15 @@ class _ConfigureDVRServerScreenState extends State { final buttonOpacity = disableFinishButton ? 0.5 : 1.0; return WillPopScope( + onWillPop: () async { + if (widget.getServer() == null) { + widget.controller.previousPage( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } + return false; + }, child: Scaffold( appBar: AppBar( leading: NavigatorPopButton( @@ -554,15 +563,6 @@ class _ConfigureDVRServerScreenState extends State { ), ), ), - onWillPop: () { - if (widget.getServer() == null) { - widget.controller.previousPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - } - return Future.value(false); - }, ); } @@ -655,6 +655,7 @@ class _LetsGoScreenState extends State { final server = widget.getServer(); return WillPopScope( + onWillPop: () async => false, child: Scaffold( appBar: showIf( isMobile, @@ -814,9 +815,6 @@ class _LetsGoScreenState extends State { ), floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, ), - onWillPop: () { - return Future.value(false); - }, ); } } diff --git a/lib/widgets/device_grid/desktop/desktop_device_grid.dart b/lib/widgets/device_grid/desktop/desktop_device_grid.dart index 0f4c64c2..b61997d2 100644 --- a/lib/widgets/device_grid/desktop/desktop_device_grid.dart +++ b/lib/widgets/device_grid/desktop/desktop_device_grid.dart @@ -561,7 +561,7 @@ class _DesktopTileViewportState extends State { const SizedBox(width: 12.0), ]), ), - if (!isSubView) + if (!isSubView && view.currentLayout.devices.contains(widget.device)) PositionedDirectional( top: 4.0, end: 4.0, diff --git a/lib/widgets/settings/settings.dart b/lib/widgets/settings/settings.dart index da0be640..fa2a8c17 100644 --- a/lib/widgets/settings/settings.dart +++ b/lib/widgets/settings/settings.dart @@ -24,12 +24,14 @@ import 'package:bluecherry_client/models/server.dart'; import 'package:bluecherry_client/providers/home_provider.dart'; import 'package:bluecherry_client/providers/server_provider.dart'; import 'package:bluecherry_client/providers/settings_provider.dart'; +import 'package:bluecherry_client/providers/update_provider.dart'; import 'package:bluecherry_client/utils/constants.dart'; import 'package:bluecherry_client/utils/extensions.dart'; -import 'package:bluecherry_client/utils/methods.dart'; import 'package:bluecherry_client/widgets/edit_server.dart'; import 'package:bluecherry_client/widgets/misc.dart'; +import 'package:bluecherry_client/widgets/settings/update.dart'; import 'package:file_picker/file_picker.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:intl/intl.dart'; @@ -63,6 +65,7 @@ class _SettingsState extends State { final loc = AppLocalizations.of(context); final theme = Theme.of(context); final settings = context.watch(); + final update = context.watch(); const divider = SliverToBoxAdapter( child: Padding( @@ -93,42 +96,57 @@ class _SettingsState extends State { SliverToBoxAdapter( child: SubHeader(loc.theme), ), - SliverList( - delegate: SliverChildListDelegate(ThemeMode.values.map((e) { - return ListTile( - leading: CircleAvatar( - backgroundColor: Colors.transparent, - foregroundColor: theme.iconTheme.color, - child: Icon({ - ThemeMode.system: Icons.brightness_auto, - ThemeMode.light: Icons.light_mode, - ThemeMode.dark: Icons.dark_mode, - }[e]!), - ), - onTap: () { + SliverList.list( + children: ThemeMode.values.map((e) { + return ListTile( + leading: CircleAvatar( + backgroundColor: Colors.transparent, + foregroundColor: theme.iconTheme.color, + child: Icon({ + ThemeMode.system: Icons.brightness_auto, + ThemeMode.light: Icons.light_mode, + ThemeMode.dark: Icons.dark_mode, + }[e]!), + ), + onTap: () { + settings.themeMode = e; + }, + trailing: Radio( + value: e, + groupValue: settings.themeMode, + onChanged: (value) { settings.themeMode = e; }, - trailing: Radio( - value: e, - groupValue: settings.themeMode, - onChanged: (value) { - settings.themeMode = e; - }, - ), - title: Text({ - ThemeMode.system: loc.system, - ThemeMode.light: loc.light, - ThemeMode.dark: loc.dark, - }[e]!), - ); - }).toList()), - ), + ), + title: Text({ + ThemeMode.system: loc.system, + ThemeMode.light: loc.light, + ThemeMode.dark: loc.dark, + }[e]!), + ); + }).toList()), + if (update.isUpdatingSupported) ...[ + divider, + SliverToBoxAdapter( + child: SubHeader( + loc.updates, + subtext: loc.runningOn(() { + if (Platform.isLinux) { + return 'Linux ${update.linuxEnvironment}'; + } else if (Platform.isWindows) { + return 'Windows'; + } + + return defaultTargetPlatform.name; + }()), + ), + ), + const SliverToBoxAdapter(child: AppUpdateCard()), + const SliverToBoxAdapter(child: AppUpdateOptions()), + ], divider, - SliverToBoxAdapter( - child: SubHeader(loc.miscellaneous), - ), - SliverList( - delegate: SliverChildListDelegate([ + SliverToBoxAdapter(child: SubHeader(loc.miscellaneous)), + SliverList.list(children: [ CorrectedListTile( iconData: Icons.message, onTap: () async { @@ -283,14 +301,14 @@ class _SettingsState extends State { ); }).toList(), ), - ])), + ]), divider, SliverToBoxAdapter(child: SubHeader(loc.dateFormat)), const SliverToBoxAdapter(child: DateFormatSection()), divider, SliverToBoxAdapter(child: SubHeader(loc.timeFormat)), - SliverList( - delegate: SliverChildListDelegate([ + SliverList.list( + children: [ 'HH:mm', 'hh:mm a', ].map((pattern) { @@ -314,7 +332,7 @@ class _SettingsState extends State { ), ), ); - }).toList())), + }).toList()), divider, // SubHeader('Language'), // SliverList( @@ -337,12 +355,7 @@ class _SettingsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 8.0), - FutureBuilder( - future: appVersion, - builder: (context, snapshot) { - return Text(snapshot.data ?? ''); - }, - ), + Text(update.packageInfo.version), const SizedBox(height: 8.0), Text( loc.versionText, diff --git a/lib/widgets/settings/update.dart b/lib/widgets/settings/update.dart new file mode 100644 index 00000000..fd9716b8 --- /dev/null +++ b/lib/widgets/settings/update.dart @@ -0,0 +1,331 @@ +/* + * This file is a part of Bluecherry Client (https://github.com/bluecherrydvr/unity). + * + * Copyright 2022 Bluecherry, LLC + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import 'package:bluecherry_client/providers/settings_provider.dart'; +import 'package:bluecherry_client/providers/update_provider.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; +import 'package:url_launcher/link.dart'; + +/// The card that displays the update information. +class AppUpdateCard extends StatelessWidget { + const AppUpdateCard({super.key}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final loc = AppLocalizations.of(context); + + final update = context.watch(); + + if (update.hasUpdateAvailable) { + final executable = update.executableFor(update.latestVersion!.version); + return Card( + margin: const EdgeInsetsDirectional.only( + start: 10.0, + end: 10.0, + bottom: 6.0, + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row(children: [ + Padding( + padding: const EdgeInsetsDirectional.only(end: 12.0), + child: Icon( + Icons.update, + size: 54.0, + color: theme.colorScheme.primary, + ), + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.newVersionAvailable, + style: theme.textTheme.titleMedium, + ), + Text(update.latestVersion!.description), + ], + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + update.latestVersion!.version, + style: theme.textTheme.labelLarge, + ), + const SizedBox(height: 6.0), + if (update.downloading) + SizedBox( + height: 32.0, + width: 32.0, + child: CircularProgressIndicator( + value: update.downloadProgress, + strokeWidth: 2.0, + ), + ) + else if (executable != null) + FilledButton( + onPressed: () => update.install( + onFail: (type) => showInstallFailDialog(context, type), + ), + child: Text(loc.installVersion), + ) + else + FilledButton( + onPressed: () => update.download( + update.latestVersion!.version, + ), + child: Text(loc.downloadVersion), + ), + ], + ), + ]), + ), + ); + } else { + return Card( + margin: const EdgeInsetsDirectional.only( + start: 10.0, + end: 10.0, + bottom: 6.0, + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row(children: [ + Padding( + padding: const EdgeInsetsDirectional.only(end: 12.0), + child: Stack(alignment: Alignment.center, children: [ + Icon( + Icons.update, + size: 54.0, + color: theme.colorScheme.primary, + ), + const Positioned( + child: DecoratedBox( + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + child: Icon( + Icons.check_circle, + color: Colors.green, + ), + ), + ), + ]), + ), + Expanded( + child: RichText( + text: TextSpan(children: [ + TextSpan( + text: '${loc.upToDate}\n', + style: theme.textTheme.headlineMedium, + ), + TextSpan( + text: loc.lastChecked( + () { + if (update.lastCheck == null) return loc.never; + if (DateUtils.isSameDay( + update.lastCheck, + DateTime.now(), + )) return loc.today; + + if (DateUtils.isSameDay( + update.lastCheck, + DateTime.now().subtract( + const Duration(days: 1, minutes: 12), + ), + )) return loc.yesterday; + + return DateFormat().format(update.lastCheck!); + }(), + ), + style: theme.textTheme.labelMedium, + ), + ]), + ), + ), + FilledButton.tonal( + onPressed: update.checkForUpdates, + child: update.loading + ? SizedBox( + height: 20.0, + width: 20.0, + child: CircularProgressIndicator.adaptive( + strokeWidth: 2.0, + semanticsLabel: loc.checkingForUpdates, + ), + ) + : Text(loc.checkForUpdates), + ), + ]), + ), + ); + } + } + + void showInstallFailDialog(BuildContext context, FailType failType) { + final loc = AppLocalizations.of(context); + + showDialog( + context: context, + builder: (context) { + late String title; + late String failMessage; + + switch (failType) { + case FailType.executableNotFound: + title = loc.failedToUpdate; + failMessage = loc.executableNotFound; + break; + } + + return AlertDialog( + title: Text(title), + content: Text(failMessage), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text(loc.ok), + ), + ], + ); + }, + ); + } +} + +class AppUpdateOptions extends StatelessWidget { + const AppUpdateOptions({super.key}); + + @override + Widget build(BuildContext context) { + final loc = AppLocalizations.of(context); + final theme = Theme.of(context); + final update = context.watch(); + return Column(children: [ + CheckboxListTile( + onChanged: (v) { + if (v != null) { + update.automaticDownloads = v; + } + }, + value: update.automaticDownloads, + secondary: CircleAvatar( + backgroundColor: Colors.transparent, + foregroundColor: theme.iconTheme.color, + child: const Icon(Icons.podcasts), + ), + title: Text(loc.automaticDownloadUpdates), + subtitle: RichText( + text: TextSpan(children: [ + TextSpan(text: loc.automaticDownloadUpdatesDescription), + TextSpan( + text: '\n${loc.learnMore}', + style: theme.textTheme.labelMedium!.copyWith( + color: theme.colorScheme.primary, + ), + mouseCursor: SystemMouseCursors.click, + recognizer: TapGestureRecognizer()..onTap = () {}, + ), + ]), + ), + isThreeLine: true, + ), + ListTile( + leading: CircleAvatar( + backgroundColor: Colors.transparent, + foregroundColor: theme.iconTheme.color, + child: const Icon(Icons.history), + ), + title: Text(loc.updateHistory), + trailing: const Icon(Icons.navigate_next), + onTap: () => showUpdateHistory(context), + ), + ]); + } + + void showUpdateHistory(BuildContext context) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + showDragHandle: true, + builder: (context) { + final update = context.watch(); + final theme = Theme.of(context); + final loc = AppLocalizations.of(context); + return DraggableScrollableSheet( + expand: false, + maxChildSize: 0.8, + initialChildSize: 0.8, + builder: (context, controller) { + return ListView.builder( + controller: controller, + itemCount: update.versions.length, + itemBuilder: (context, index) { + final version = update.versions.reversed.elementAt(index); + return ListTile( + title: Row(children: [ + RichText( + text: TextSpan(children: [ + TextSpan(text: version.version), + const TextSpan(text: ' '), + TextSpan( + text: SettingsProvider.instance.dateFormat.format( + DateFormat('EEE, d MMM yyyy') + .parse(version.publishedAt), + ), + style: theme.textTheme.labelSmall, + ), + ]), + ), + const Expanded( + child: Padding( + padding: EdgeInsetsDirectional.only(start: 12.0), + child: Divider(), + ), + ), + ]), + subtitle: Text(version.description), + isThreeLine: true, + trailing: Link( + uri: Uri.parse( + 'https://github.com/bluecherrydvr/unity/releases/tag/v${version.version}'), + builder: (context, followLink) { + return TextButton( + onPressed: followLink, + child: Text(loc.learnMore), + ); + }, + ), + ); + }, + ); + }, + ); + }, + ); + } +} diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 37aabe4f..3d2be216 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -90,6 +90,7 @@ set_target_properties(${BINARY_NAME} # them to the application. include(flutter/generated_plugins.cmake) +target_link_libraries(${BINARY_NAME} PRIVATE ${MIMALLOC_LIB}) # === Installation === # By default, "installing" just makes a relocatable bundle in the build diff --git a/linux/env/appimage.json b/linux/env/appimage.json new file mode 100644 index 00000000..39b8865d --- /dev/null +++ b/linux/env/appimage.json @@ -0,0 +1,3 @@ +{ + "linux_environment": "AppImage" +} \ No newline at end of file diff --git a/linux/env/deb.json b/linux/env/deb.json new file mode 100644 index 00000000..27e36e56 --- /dev/null +++ b/linux/env/deb.json @@ -0,0 +1,3 @@ +{ + "linux_environment": "deb" +} \ No newline at end of file diff --git a/linux/env/rpm.json b/linux/env/rpm.json new file mode 100644 index 00000000..a8346794 --- /dev/null +++ b/linux/env/rpm.json @@ -0,0 +1,3 @@ +{ + "linux_environment": "rpm" +} \ No newline at end of file diff --git a/linux/env/tar.gz.json b/linux/env/tar.gz.json new file mode 100644 index 00000000..b9f43953 --- /dev/null +++ b/linux/env/tar.gz.json @@ -0,0 +1,3 @@ +{ + "linux_environment": "tar.gz" +} \ No newline at end of file diff --git a/packages/unity_multi_window/example/pubspec.lock b/packages/unity_multi_window/example/pubspec.lock index 13724f04..ec963e1c 100644 --- a/packages/unity_multi_window/example/pubspec.lock +++ b/packages/unity_multi_window/example/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" fake_async: dependency: transitive description: @@ -58,47 +58,39 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" lints: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.1" matcher: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "586678f20e112219ed0f73215f01bcdf1d769824ba2ebae45ad918a9bfde9bdb" + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.5.0" meta: dependency: transitive description: @@ -132,18 +124,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -164,10 +156,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.1" unity_multi_window: dependency: "direct main" description: @@ -183,6 +175,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" sdks: - dart: ">=3.0.0-0 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=1.17.0" diff --git a/packages/unity_video_player/unity_video_player/pubspec.yaml b/packages/unity_video_player/unity_video_player/pubspec.yaml index 6339869d..5b0c2c02 100644 --- a/packages/unity_video_player/unity_video_player/pubspec.yaml +++ b/packages/unity_video_player/unity_video_player/pubspec.yaml @@ -24,7 +24,7 @@ dependency_overrides: media_kit: git: url: https://github.com/alexmercerind/media_kit.git - ref: 7c95f7e0a140fc33ce4880e78f396d2ac9333b81 + ref: 0d579f626e1d85b5243c879511b38ccdddf722cf path: media_kit/ flutter: diff --git a/packages/unity_video_player/unity_video_player_media_kit/pubspec.yaml b/packages/unity_video_player/unity_video_player_media_kit/pubspec.yaml index c97e2121..426e1b2b 100644 --- a/packages/unity_video_player/unity_video_player_media_kit/pubspec.yaml +++ b/packages/unity_video_player/unity_video_player_media_kit/pubspec.yaml @@ -18,42 +18,42 @@ dependencies: media_kit: 0.0.11 # git: # url: https://github.com/alexmercerind/media_kit.git - # ref: 7c95f7e0a140fc33ce4880e78f396d2ac9333b81 + # ref: 0d579f626e1d85b5243c879511b38ccdddf722cf # path: media_kit/ media_kit_native_event_loop: git: url: https://github.com/alexmercerind/media_kit.git - ref: 7c95f7e0a140fc33ce4880e78f396d2ac9333b81 + ref: 0d579f626e1d85b5243c879511b38ccdddf722cf path: media_kit_native_event_loop/ media_kit_video: git: url: https://github.com/alexmercerind/media_kit.git - ref: 7c95f7e0a140fc33ce4880e78f396d2ac9333b81 + ref: 0d579f626e1d85b5243c879511b38ccdddf722cf path: media_kit_video/ media_kit_libs_windows_video: git: url: https://github.com/alexmercerind/media_kit.git - ref: 7c95f7e0a140fc33ce4880e78f396d2ac9333b81 + ref: 0d579f626e1d85b5243c879511b38ccdddf722cf path: libs/windows/media_kit_libs_windows_video/ media_kit_libs_linux: git: url: https://github.com/alexmercerind/media_kit.git - ref: 7c95f7e0a140fc33ce4880e78f396d2ac9333b81 + ref: 0d579f626e1d85b5243c879511b38ccdddf722cf path: libs/linux/media_kit_libs_linux/ media_kit_libs_macos_video: git: url: https://github.com/alexmercerind/media_kit.git - ref: 7c95f7e0a140fc33ce4880e78f396d2ac9333b81 + ref: 0d579f626e1d85b5243c879511b38ccdddf722cf path: libs/macos/media_kit_libs_macos_video/ media_kit_libs_android_video: git: url: https://github.com/alexmercerind/media_kit.git - ref: 7c95f7e0a140fc33ce4880e78f396d2ac9333b81 + ref: 0d579f626e1d85b5243c879511b38ccdddf722cf path: libs/android/media_kit_libs_android_video/ media_kit_libs_ios_video: git: url: https://github.com/alexmercerind/media_kit.git - ref: 7c95f7e0a140fc33ce4880e78f396d2ac9333b81 + ref: 0d579f626e1d85b5243c879511b38ccdddf722cf path: libs/ios/media_kit_libs_ios_video/ unity_video_player_platform_interface: path: ../unity_video_player_platform_interface/ @@ -68,7 +68,7 @@ dependency_overrides: media_kit: git: url: https://github.com/alexmercerind/media_kit.git - ref: 7c95f7e0a140fc33ce4880e78f396d2ac9333b81 + ref: 0d579f626e1d85b5243c879511b38ccdddf722cf path: media_kit/ flutter: diff --git a/packages/unity_video_player/unity_video_player_platform_interface/lib/unity_video_player_platform_interface.dart b/packages/unity_video_player/unity_video_player_platform_interface/lib/unity_video_player_platform_interface.dart index 49c4f7b5..074399d3 100644 --- a/packages/unity_video_player/unity_video_player_platform_interface/lib/unity_video_player_platform_interface.dart +++ b/packages/unity_video_player/unity_video_player_platform_interface/lib/unity_video_player_platform_interface.dart @@ -67,6 +67,16 @@ abstract class UnityVideoPlayerInterface extends PlatformInterface { static void unregisterPlayer(UnityVideoPlayer player) { _appPlayers.remove(player); } + + /// Diposes all the player instances + static Future dispose() { + return Future.microtask(() async { + for (final player in UnityVideoPlayerInterface.players.toList()) { + debugPrint('Disposing player ${player.hashCode}'); + await player.dispose(); + } + }); + } } typedef VideoViewInheritance = _UnityVideoView; diff --git a/pubspec.lock b/pubspec.lock index 0ed31657..c45373b4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -52,12 +52,11 @@ packages: awesome_notifications: dependency: "direct main" description: - path: "." - ref: HEAD - resolved-ref: "7513b844fe9e70b9b9400345a2348ee863329e4d" - url: "https://github.com/HannesGitH/awesome_notifications.git" - source: git - version: "0.7.4" + name: awesome_notifications + sha256: "6ba98d73553c8a54e7b77f8dd8b95ce9d32a7839ef182b28cf2ad54ec28b1821" + url: "https://pub.dev" + source: hosted + version: "0.7.5-dev.3" boolean_selector: dependency: transitive description: @@ -102,10 +101,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" console: dependency: transitive description: @@ -142,10 +141,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: f52ab3b76b36ede4d135aab80194df8925b553686f0fa12226b4e2d658e45903 + sha256: "86add5ef97215562d2e090535b0a16f197902b10c369c558a100e74ea06e8659" url: "https://pub.dev" source: hosted - version: "8.2.2" + version: "9.0.3" device_info_plus_platform_interface: dependency: transitive description: @@ -158,18 +157,18 @@ packages: dependency: "direct main" description: name: dio - sha256: a9d76e72985d7087eb7c5e7903224ae52b337131518d127c554b9405936752b8 + sha256: ce75a1b40947fea0a0e16ce73337122a86762e38b982e1ccb909daa3b9bc4197 url: "https://pub.dev" source: hosted - version: "5.2.1+1" + version: "5.3.2" duration: dependency: "direct main" description: name: duration - sha256: d0b29d0a345429e3986ac56d60e4aef65b37d11e653022b2b9a4b361332b777f + sha256: "0548a12d235dab185c677ef660995f23fdc06a02a2b984aa23805f6a03d82815" url: "https://pub.dev" source: hosted - version: "3.0.12" + version: "3.0.13" fake_async: dependency: transitive description: @@ -190,18 +189,18 @@ packages: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" file_picker: dependency: "direct main" description: name: file_picker - sha256: "9d6e95ec73abbd31ec54d0e0df8a961017e165aba1395e462e5b31ea0c165daf" + sha256: "21145c9c268d54b1f771d8380c195d2d6f655e0567dc1ca2f9c134c02c819e0a" url: "https://pub.dev" source: hosted - version: "5.3.1" + version: "5.3.3" firebase_core: dependency: "direct main" description: @@ -267,10 +266,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" flutter_localizations: dependency: "direct main" description: flutter @@ -330,10 +329,10 @@ packages: dependency: "direct main" description: name: http - sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "0.13.6" + version: "1.1.0" http_parser: dependency: transitive description: @@ -378,10 +377,10 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.1" matcher: dependency: transitive description: @@ -402,74 +401,74 @@ packages: dependency: "direct overridden" description: path: media_kit - ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" - resolved-ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" + ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" + resolved-ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" url: "https://github.com/alexmercerind/media_kit.git" source: git - version: "1.1.0" + version: "1.1.3+1" media_kit_libs_android_video: dependency: transitive description: path: "libs/android/media_kit_libs_android_video" - ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" - resolved-ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" + ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" + resolved-ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" url: "https://github.com/alexmercerind/media_kit.git" source: git - version: "1.2.0" + version: "1.3.1" media_kit_libs_ios_video: dependency: transitive description: path: "libs/ios/media_kit_libs_ios_video" - ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" - resolved-ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" + ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" + resolved-ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" url: "https://github.com/alexmercerind/media_kit.git" source: git - version: "1.0.5" + version: "1.1.2" media_kit_libs_linux: dependency: transitive description: path: "libs/linux/media_kit_libs_linux" - ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" - resolved-ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" + ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" + resolved-ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" url: "https://github.com/alexmercerind/media_kit.git" source: git - version: "1.0.2" + version: "1.1.0" media_kit_libs_macos_video: dependency: transitive description: path: "libs/macos/media_kit_libs_macos_video" - ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" - resolved-ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" + ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" + resolved-ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" url: "https://github.com/alexmercerind/media_kit.git" source: git - version: "1.0.6" + version: "1.1.2" media_kit_libs_windows_video: dependency: transitive description: path: "libs/windows/media_kit_libs_windows_video" - ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" - resolved-ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" + ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" + resolved-ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" url: "https://github.com/alexmercerind/media_kit.git" source: git - version: "1.0.4" + version: "1.0.6" media_kit_native_event_loop: dependency: transitive description: path: media_kit_native_event_loop - ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" - resolved-ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" + ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" + resolved-ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" url: "https://github.com/alexmercerind/media_kit.git" source: git - version: "1.0.6" + version: "1.0.7" media_kit_video: dependency: transitive description: path: media_kit_video - ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" - resolved-ref: "7c95f7e0a140fc33ce4880e78f396d2ac9333b81" + ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" + resolved-ref: "0d579f626e1d85b5243c879511b38ccdddf722cf" url: "https://github.com/alexmercerind/media_kit.git" source: git - version: "1.1.0" + version: "1.1.3" meta: dependency: transitive description: @@ -482,10 +481,10 @@ packages: dependency: "direct dev" description: name: msix - sha256: "927c207656dc92eaf3d005c734d259c4ede440052a7281f9e1cc8966ffdbf1c7" + sha256: "76c87b8207323803169626a55afd78bbb8413c984df349a76598b9fbf9224677" url: "https://pub.dev" source: hosted - version: "3.16.0" + version: "3.16.1" nested: dependency: transitive description: @@ -503,13 +502,13 @@ packages: source: hosted version: "2.1.0" package_info_plus: - dependency: transitive + dependency: "direct main" description: name: package_info_plus - sha256: ceb027f6bc6a60674a233b4a90a7658af1aebdea833da0b5b53c1e9821a78c7b + sha256: "6ff267fcd9d48cb61c8df74a82680e8b82e940231bb5f68356672fde0397334a" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.1.0" package_info_plus_platform_interface: dependency: transitive description: @@ -538,82 +537,82 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" + sha256: "5d44fc3314d969b84816b569070d7ace0f1dea04bd94a83f74c4829615d22ad8" url: "https://pub.dev" source: hosted - version: "2.0.27" + version: "2.1.0" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" + sha256: "1b744d3d774e5a879bb76d6cd1ecee2ba2c6960c03b1020cd35212f6aa267ac5" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.0" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 + sha256: ba2b77f0c52a33db09fc8caf85b12df691bf28d983e84cf87ff6d693cfa007b3 url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.2.0" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + sha256: bced5679c7df11190e1ddc35f3222c858f328fff85c3942e46e7f5589bf9eb84 url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.1.0" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" + sha256: ee0e0d164516b90ae1f970bdf29f726f1aa730d7cfc449ecc74c495378b705da url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.2.0" permission_handler: dependency: "direct main" description: name: permission_handler - sha256: "1b6b3e73f0bcbc856548bbdfb1c33084a401c4f143e220629a9055233d76c331" + sha256: "63e5216aae014a72fe9579ccd027323395ce7a98271d9defa9d57320d001af81" url: "https://pub.dev" source: hosted - version: "10.3.0" + version: "10.4.3" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "8f6a95ccbca13766882f95d32684d7c9bfe6c45650c32bedba948ef1c6a4ddf7" + sha256: "2ffaf52a21f64ac9b35fe7369bb9533edbd4f698e5604db8645b1064ff4cf221" url: "https://pub.dev" source: hosted - version: "10.2.3" + version: "10.3.3" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "08dcb6ce628ac0b257e429944b4c652c2a4e6af725bdf12b498daa2c6b2b1edb" + sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" url: "https://pub.dev" source: hosted - version: "9.1.0" + version: "9.1.4" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: de20a5c3269229c1ae2e5a6b822f6cb59578b23e8255c93fbeebfc82116e6b11 + sha256: "7c6b1500385dd1d2ca61bb89e2488ca178e274a69144d26bbd65e33eae7c02a9" url: "https://pub.dev" source: hosted - version: "3.10.0" + version: "3.11.3" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b + sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 url: "https://pub.dev" source: hosted - version: "0.1.2" + version: "0.1.3" petitparser: dependency: transitive description: @@ -634,10 +633,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" pointycastle: dependency: transitive description: @@ -646,14 +645,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.7.3" - process: - dependency: transitive - description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" - source: hosted - version: "4.2.4" provider: dependency: "direct main" description: @@ -767,18 +758,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -807,10 +798,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" typed_data: dependency: transitive description: @@ -867,18 +858,18 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3 + sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e" url: "https://pub.dev" source: hosted - version: "6.1.11" + version: "6.1.12" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: eed4e6a1164aa9794409325c3b707ff424d4d1c2a785e7db67f8bbda00e36e51 + sha256: "78cb6dea3e93148615109e58e42c35d1ffbf5ef66c44add673d0ab75f12ff3af" url: "https://pub.dev" source: hosted - version: "6.0.35" + version: "6.0.37" url_launcher_ios: dependency: transitive description: @@ -899,34 +890,34 @@ packages: dependency: transitive description: name: url_launcher_macos - sha256: "91ee3e75ea9dadf38036200c5d3743518f4a5eb77a8d13fda1ee5764373f185e" + sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.6" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" + sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "6bb1e5d7fe53daf02a8fee85352432a40b1f868a81880e99ec7440113d5cfcab" + sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4 url: "https://pub.dev" source: hosted - version: "2.0.17" + version: "2.0.18" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771" + sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.7" uuid: dependency: "direct main" description: @@ -943,6 +934,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + version: + dependency: "direct main" + description: + name: version + sha256: "3d4140128e6ea10d83da32fef2fa4003fccbf6852217bb854845802f04191f94" + url: "https://pub.dev" + source: hosted + version: "3.0.2" volume_controller: dependency: transitive description: @@ -979,26 +978,34 @@ packages: dependency: transitive description: name: win32 - sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" + sha256: f2add6fa510d3ae152903412227bda57d0d5a8da61d2c39c1fb022c9429a41c0 url: "https://pub.dev" source: hosted - version: "4.1.4" + version: "5.0.6" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9 + url: "https://pub.dev" + source: hosted + version: "1.1.1" window_manager: dependency: "direct main" description: name: window_manager - sha256: "95096fede562cbb65f30d38b62d819a458f59ba9fe4a317f6cee669710f6676b" + sha256: "9eef00e393e7f9308309ce9a8b2398c9ee3ca78b50c96e8b4f9873945693ac88" url: "https://pub.dev" source: hosted - version: "0.3.4" + version: "0.3.5" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.1" xml: dependency: transitive description: @@ -1011,10 +1018,10 @@ packages: dependency: "direct main" description: name: xml2json - sha256: c8cb35b83cce879c2ea86951fd257f4e765b0030a0298b35cf94f2b3d0f32095 + sha256: "1cd5d23474f38e794fbcab262290bc638c59c985c34d29180fc4525eda24ea7d" url: "https://pub.dev" source: hosted - version: "5.3.6" + version: "6.2.0" yaml: dependency: transitive description: @@ -1025,4 +1032,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.1.0-185.0.dev <4.0.0" - flutter: ">=3.7.0" + flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index 0e14bc2c..960f41d7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,12 +16,12 @@ dependencies: unity_video_player: path: packages/unity_video_player/unity_video_player/ - http: ^0.13.5 + http: ^1.1.0 # Dio is used by DownloadManager to show the donwload progress of the file. This is currently not possible by using http # TODO: no longer use dio and find a solution to show donwload progress using the http package - dio: ^5.1.1 + dio: ^5.3.2 - xml2json: ^5.3.5 + xml2json: ^6.2.0 auto_size_text: ^3.0.0 animations: ^2.0.7 provider: ^6.0.5 @@ -29,21 +29,19 @@ dependencies: flutter_simple_treeview: ^3.0.2 sliver_tools: ^0.2.12 - intl: ^0.18.0 + intl: ^0.18.1 duration: ^3.0.12 firebase_core: 2.10.0 firebase_messaging: ^14.4.1 - # Use HannesGitH branch until https://github.com/rafaelsetragni/awesome_notifications/pull/727 is merged - # TODO: remove this - awesome_notifications: - git: - url: https://github.com/HannesGitH/awesome_notifications.git + awesome_notifications: ^0.7.5-dev.3 - device_info_plus: ^8.2.0 + device_info_plus: ^9.0.3 + package_info_plus: ^4.1.0 + version: ^3.0.2 url_launcher: ^6.1.10 path_provider: ^2.0.14 - file_picker: ^5.2.10 + file_picker: ^5.3.3 safe_local_storage: ^1.0.0 # Hive is just used in terms of migration @@ -69,7 +67,7 @@ dependency_overrides: media_kit: git: url: https://github.com/alexmercerind/media_kit.git - ref: 7c95f7e0a140fc33ce4880e78f396d2ac9333b81 + ref: 0d579f626e1d85b5243c879511b38ccdddf722cf path: media_kit/ flutter: @@ -79,7 +77,6 @@ flutter: generate: true assets: - assets/images/ - - version.txt flutter_icons: # android: "launcher_icon" diff --git a/version.txt b/version.txt deleted file mode 100644 index 04ec4eb5..00000000 --- a/version.txt +++ /dev/null @@ -1 +0,0 @@ -#define MyAppVersion "3.0.0-beta8"