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