feat: OTA update

pull/25/head
Frédérik Benoist 2023-10-16 16:47:04 +02:00
parent 665a2b6523
commit 996c717fb9
10 changed files with 280 additions and 10 deletions

View File

@ -30,6 +30,15 @@
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
<provider
android:name="sk.fourq.otaupdate.OtaUpdateFileProvider"
android:authorities="${applicationId}.ota_update_provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application> </application>
<!-- Permissions options for the `storage` group --> <!-- Permissions options for the `storage` group -->
@ -39,6 +48,7 @@
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<!-- Permissions options for the `camera` group --> <!-- Permissions options for the `camera` group -->
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.CAMERA"/>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="internal_apk_storage" path="ota_update/"/>
</paths>

View File

@ -45,3 +45,7 @@ const String LOGIN_API = 'https://mp4.ikksgroup.com' + "/authentication/login";
const String PRODUCT_API = 'https://mp4.ikksgroup.com' + "/example/getProduct"; const String PRODUCT_API = 'https://mp4.ikksgroup.com' + "/example/getProduct";
const String SERVLET_API = const String SERVLET_API =
'https://mp4.ikksgroup.com' + "/MobilePortal4_external/UploadPhotoServlet"; 'https://mp4.ikksgroup.com' + "/MobilePortal4_external/UploadPhotoServlet";
// https://blogit.q2ii.fr
// https://mp4.ikksgroup.com
const String DOMAIN_CHECK_VERSION = 'https://mp4.ikksgroup.com';

View File

@ -481,4 +481,24 @@ class ApiProvider {
void close() { void close() {
_dio.close(); _dio.close();
} }
Future<bool> checkAppVersion() async {
try {
final response = await http
.get(Uri.parse(DOMAIN_CHECK_VERSION + '/deploy/mobdr/version.json'));
final data = jsonDecode(response.body);
if (data.containsKey('latest_version')) {
if (data['latest_version'] != SharedPrefs().appVersion) {
// The versions differ
return true;
}
}
} catch (e) {
print('Error verifying the version : $e');
}
// If an error has occurred or if the versions are identical
return false;
}
} }

View File

@ -11,6 +11,7 @@ import 'package:mobdr/ui/account/settings.dart';
import 'package:mobdr/ui/account/log.dart'; import 'package:mobdr/ui/account/log.dart';
import 'package:mobdr/ui/general/notification.dart'; import 'package:mobdr/ui/general/notification.dart';
import 'package:mobdr/ui/reusable/reusable_widget.dart'; import 'package:mobdr/ui/reusable/reusable_widget.dart';
import 'package:mobdr/ui/appstore/updatecheck.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mobdr/ui/authentication/signin.dart'; import 'package:mobdr/ui/authentication/signin.dart';
import 'dart:convert'; import 'dart:convert';
@ -78,6 +79,8 @@ class _TabAccountPageState extends State<TabAccountPage>
_reusableWidget.divider1(), _reusableWidget.divider1(),
_createListMenu('Show logs', LogPage()), _createListMenu('Show logs', LogPage()),
_reusableWidget.divider1(), _reusableWidget.divider1(),
_createListMenu('Check version', UpdateCheckPage()),
_reusableWidget.divider1(),
Container( Container(
margin: EdgeInsets.fromLTRB(0, 18, 0, 0), margin: EdgeInsets.fromLTRB(0, 18, 0, 0),
child: GestureDetector( child: GestureDetector(

View File

@ -0,0 +1,175 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:ota_update/ota_update.dart';
import 'package:http/http.dart' as http;
import 'package:mobdr/service/shared_prefs.dart';
import 'package:mobdr/config/constant.dart';
class UpdateCheckPage extends StatefulWidget {
@override
_UpdateCheckPageState createState() => _UpdateCheckPageState();
}
class _UpdateCheckPageState extends State<UpdateCheckPage> {
OtaEvent? currentEvent = null;
String _currentVersion = SharedPrefs().appVersion;
String _latestVersion = '';
String _releaseDate = '';
List<String> _releaseNotes = [];
String _downloadLink = '';
String _checksum = '';
@override
void initState() {
super.initState();
_fetchLatestVersion();
}
Future<void> _fetchLatestVersion() async {
try {
final response = await http
.get(Uri.parse(DOMAIN_CHECK_VERSION + '/deploy/mobdr/version.json'));
final data = jsonDecode(response.body);
setState(() {
_latestVersion = data['latest_version'];
_releaseDate = data['release_date'];
_releaseNotes = List<String>.from(data['release_notes']);
_downloadLink = data['download_link'];
_checksum = data['checksum'];
});
} catch (e) {
print('Error retrieving latest version : $e');
}
}
Future<void> _doUpdate() async {
try {
OtaUpdate()
.execute(
_downloadLink,
// OPTIONAL
destinationFilename: 'app_release.apk',
//OPTIONAL, ANDROID ONLY - ABILITY TO VALIDATE CHECKSUM OF FILE:
sha256checksum: _checksum,
)
.listen(
(OtaEvent event) {
setState(() => currentEvent = event);
},
);
} catch (e) {
print('Failed to make OTA update. Details: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Checking for update'),
),
body: Center(
child: currentEvent == null
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Current version : ',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
Text(
_currentVersion,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 22,
color: Colors.red,
),
),
SizedBox(height: 16),
Text(
'Version available : ',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
Text(
_latestVersion,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
color: Colors.green,
),
),
if (_currentVersion != _latestVersion) ...[
SizedBox(height: 16),
Text(
'Publication date : $_releaseDate',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
'Release notes :',
style: TextStyle(fontWeight: FontWeight.bold),
),
for (String note in _releaseNotes) ...[
SizedBox(height: 4),
Text('- $note'),
],
SizedBox(height: 16),
ElevatedButton(
onPressed: _doUpdate,
child: Text(
'Update',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
),
),
] else ...[
SizedBox(height: 32),
Text(
'You have the latest version',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
],
],
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'OTA status',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
),
),
SizedBox(height: 32),
Text('${currentEvent!.status}'),
SizedBox(height: 16),
Text(
'${currentEvent!.value}',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
)
],
)),
);
}
}

View File

@ -7,6 +7,7 @@ import 'package:mobdr/config/global_style.dart';
import 'package:mobdr/main.dart'; import 'package:mobdr/main.dart';
import 'package:mobdr/events.dart'; import 'package:mobdr/events.dart';
import 'package:mobdr/ui/reusable/reusable_widget.dart'; import 'package:mobdr/ui/reusable/reusable_widget.dart';
import 'package:mobdr/ui/appstore/updatecheck.dart';
import 'package:mobdr/service/shared_prefs.dart'; import 'package:mobdr/service/shared_prefs.dart';
import 'package:mobdr/network/api_provider.dart'; import 'package:mobdr/network/api_provider.dart';
import 'package:mobdr/db/box_visit_photo.dart'; import 'package:mobdr/db/box_visit_photo.dart';
@ -71,7 +72,43 @@ class _SynchronizationPageState extends State<SynchronizationPage>
_isInternetConnexion = true; _isInternetConnexion = true;
// synchronization start // synchronization start
startSync(); await startSync();
// Vérifier si une mise à jour est disponible
bool isUpdateAvailable = await _apiProvider.checkAppVersion();
if (isUpdateAvailable) {
// Afficher le message de mise à jour
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("New version available"),
content: Text(
"A new version is available. Would you like to install it?"),
actions: [
TextButton(
child: Text("No"),
onPressed: () {
Navigator.pop(context); // Closes the dialog box
},
),
TextButton(
child: Text("Yes"),
onPressed: () {
Navigator.pop(context); // Closes the dialog box
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UpdateCheckPage()),
);
},
),
],
);
},
);
}
} }
} }
@ -200,7 +237,7 @@ class _SynchronizationPageState extends State<SynchronizationPage>
} }
} }
void startSync() async { Future<void> startSync() async {
// disable navigation bar buttons // disable navigation bar buttons
eventBus.fire(SynchronizationEvent(true)); eventBus.fire(SynchronizationEvent(true));

View File

@ -1,4 +1,6 @@
PODS: PODS:
- audioplayers_darwin (0.0.1):
- FlutterMacOS
- connectivity_plus (0.0.1): - connectivity_plus (0.0.1):
- FlutterMacOS - FlutterMacOS
- ReachabilitySwift - ReachabilitySwift
@ -28,13 +30,14 @@ PODS:
- FlutterMacOS - FlutterMacOS
DEPENDENCIES: DEPENDENCIES:
- audioplayers_darwin (from `Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos`)
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`) - connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
- FlutterMacOS (from `Flutter/ephemeral`) - FlutterMacOS (from `Flutter/ephemeral`)
- objectbox_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/objectbox_flutter_libs/macos`) - objectbox_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/objectbox_flutter_libs/macos`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`) - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
- wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`) - wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`)
@ -45,6 +48,8 @@ SPEC REPOS:
- ReachabilitySwift - ReachabilitySwift
EXTERNAL SOURCES: EXTERNAL SOURCES:
audioplayers_darwin:
:path: Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos
connectivity_plus: connectivity_plus:
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos
device_info_plus: device_info_plus:
@ -56,15 +61,16 @@ EXTERNAL SOURCES:
package_info_plus: package_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
path_provider_foundation: path_provider_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
shared_preferences_foundation: shared_preferences_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
sqflite: sqflite:
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos :path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos
wakelock_macos: wakelock_macos:
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos :path: Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos
SPEC CHECKSUMS: SPEC CHECKSUMS:
audioplayers_darwin: dcad41de4fbd0099cb3749f7ab3b0cb8f70b810c
connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24

View File

@ -429,10 +429,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: ffi name: ffi
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "2.0.2"
file: file:
dependency: transitive dependency: transitive
description: description:
@ -781,6 +781,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.2" version: "1.0.2"
ota_update:
dependency: "direct main"
description:
name: ota_update
sha256: "7ce0af2119458cb7cc3122ecdc4e18ec3c0108162bfab9a4b064d686e1a5fc22"
url: "https://pub.dev"
source: hosted
version: "5.1.0"
package_config: package_config:
dependency: transitive dependency: transitive
description: description:

View File

@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 0.0.1+1 version: 0.2.3+1
environment: environment:
sdk: '>=2.19.0 <3.0.0' sdk: '>=2.19.0 <3.0.0'
@ -99,7 +99,7 @@ dependencies:
# https://pub.dev/packages/device_info_plus/ # https://pub.dev/packages/device_info_plus/
device_info_plus: ^8.2.0 device_info_plus: ^8.2.0
# https://pwebview_flutterub.dev/packages/webview_flutter/ # https://pub.dev/packages/webview_flutter/
# MP4 # MP4
webview_flutter: ^4.2.1 webview_flutter: ^4.2.1
@ -121,6 +121,9 @@ dependencies:
# https://pub.dev/packages/permission_handler # https://pub.dev/packages/permission_handler
permission_handler: 10.3.0 permission_handler: 10.3.0
# https://pub.dev/packages/ota_update
ota_update: ^5.1.0
# hhttps://pub.dev/packages/timelines # hhttps://pub.dev/packages/timelines
#timelines: ^0.1.0 #timelines: ^0.1.0