From f43332ca97f19bc963d71798db70136046a88d28 Mon Sep 17 00:00:00 2001 From: Viktor Date: Tue, 23 Mar 2021 14:34:21 +0100 Subject: [PATCH 1/7] added nullsafety canUpdate added version check --- lib/new_version.dart | 92 +++++++++++++++++++++++--------------------- pubspec.yaml | 4 +- 2 files changed, 51 insertions(+), 45 deletions(-) diff --git a/lib/new_version.dart b/lib/new_version.dart index 3664a74..2cabb0b 100644 --- a/lib/new_version.dart +++ b/lib/new_version.dart @@ -1,85 +1,98 @@ library new_version; +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:html/parser.dart' show parse; import 'package:http/http.dart' as http; import 'package:package_info_plus/package_info_plus.dart'; -import 'package:html/parser.dart' show parse; -import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; -import 'package:flutter/cupertino.dart'; -import 'dart:convert'; -import 'dart:async'; /// Information about the app's current version, and the most recent version /// available in the Apple App Store or Google Play Store. class VersionStatus { /// True if the there is a more recent version of the app in the store. - bool canUpdate; + bool get canUpdate { + if (storeVersion == null) { + return false; + } + if (localVersion != null) { + final current = int.tryParse(localVersion!.replaceAll('.', '')); + final store = int.tryParse(storeVersion!.replaceAll('.', '')); + if (current != null && store != null) { + return store > current; + } + } + return storeVersion != localVersion; + } /// The current version of the app. - String localVersion; + String? localVersion; /// The most recent version of the app in the store. - String storeVersion; + String? storeVersion; /// A link to the app store page where the app can be updated. - String appStoreLink; + String? appStoreLink; - VersionStatus({this.canUpdate, this.localVersion, this.storeVersion}); + VersionStatus({this.localVersion, this.storeVersion}); } class NewVersion { /// This is required to check the user's platform and display alert dialogs. - BuildContext context; + final BuildContext context; /// An optional value that can override the default packageName when /// attempting to reach the Google Play Store. This is useful if your app has /// a different package name in the Play Store. - String androidId; + final String? androidId; /// An optional value that can override the default packageName when /// attempting to reach the Apple App Store. This is useful if your app has /// a different package name in the App Store. - String iOSId; + final String? iOSId; /// An optional value that can override the default callback to dismiss button. - VoidCallback dismissAction; + final VoidCallback? dismissAction; /// An optional value that can override the default text to alert, /// you can ${versionStatus.localVersion} to ${versionStatus.storeVersion} /// to determinate in the text a versions. - String dialogText; + final String? dialogText; /// An optional value that can override the default title of alert dialog. - String dialogTitle; + final String dialogTitle; /// An optional value that can override the default text of dismiss button. - String dismissText; + final String dismissText; /// An optional value that can override the default text of update button. - String updateText; + final String updateText; /// Only affects iOS App Store lookup: The two-letter country code for the store you want to search. /// Provide a value here if your app is only available outside the US. /// For example: US. The default is US. /// See http://en.wikipedia.org/wiki/ ISO_3166-1_alpha-2 for a list of ISO Country Codes. - String iOSAppStoreCountry; + final String? iOSAppStoreCountry; NewVersion({ this.androidId, this.iOSId, - @required this.context, + required this.context, this.dismissAction, this.dismissText: 'Maybe Later', this.updateText: 'Update', this.dialogText, this.dialogTitle: 'Update Available', this.iOSAppStoreCountry, - }) : assert(context != null); + }); /// This checks the version status, then displays a platform-specific alert /// with buttons to dismiss the update alert, or go to the app store. showAlertIfNecessary() async { - VersionStatus versionStatus = await getVersionStatus(); + final VersionStatus? versionStatus = await getVersionStatus(); if (versionStatus != null && versionStatus.canUpdate) { showUpdateDialog(versionStatus); } @@ -88,11 +101,9 @@ class NewVersion { /// This checks the version status and returns the information. This is useful /// if you want to display a custom alert, or use the information in a different /// way. - Future getVersionStatus() async { + Future getVersionStatus() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); - VersionStatus versionStatus = VersionStatus( - localVersion: packageInfo.version, - ); + VersionStatus versionStatus = VersionStatus(localVersion: packageInfo.version); switch (Theme.of(context).platform) { case TargetPlatform.android: final id = androidId ?? packageInfo.packageName; @@ -103,13 +114,9 @@ class NewVersion { versionStatus = await _getiOSStoreVersion(id, versionStatus); break; default: - print('This target platform is not yet supported by this package.'); - } - if (versionStatus == null) { - return null; + debugPrint('This target platform is not yet supported by this package.'); } - versionStatus.canUpdate = - versionStatus.storeVersion != versionStatus.localVersion; + return versionStatus; } @@ -118,12 +125,12 @@ class NewVersion { _getiOSStoreVersion(String id, VersionStatus versionStatus) async { final parameters = {"bundleId": "$id"}; if (iOSAppStoreCountry != null) { - parameters.addAll({"country": iOSAppStoreCountry}); + parameters.addAll({"country": iOSAppStoreCountry!}); } var uri = Uri.https("itunes.apple.com", "/lookup", parameters); final response = await http.get(uri); if (response.statusCode != 200) { - print('Can\'t find an app in the App Store with the id: $id'); + debugPrint('Can\'t find an app in the App Store with the id: $id'); return null; } final jsonObj = json.decode(response.body); @@ -134,19 +141,18 @@ class NewVersion { /// Android info is fetched by parsing the html of the app store page. _getAndroidStoreVersion(String id, VersionStatus versionStatus) async { - final uri = - Uri.https("play.google.com", "/store/apps/details", {"id": "$id"}); + final uri = Uri.https("play.google.com", "/store/apps/details", {"id": "$id"}); final response = await http.get(uri); if (response.statusCode != 200) { - print('Can\'t find an app in the Play Store with the id: $id'); + debugPrint('Can\'t find an app in the Play Store with the id: $id'); return null; } final document = parse(response.body); final elements = document.getElementsByClassName('hAyfc'); final versionElement = elements.firstWhere( - (elm) => elm.querySelector('.BgcNfc').text == 'Current Version', + (elm) => elm.querySelector('.BgcNfc')!.text == 'Current Version', ); - versionStatus.storeVersion = versionElement.querySelector('.htlgb').text; + versionStatus.storeVersion = versionElement.querySelector('.htlgb')!.text; versionStatus.appStoreLink = uri.toString(); return versionStatus; } @@ -160,11 +166,11 @@ class NewVersion { 'You can now update this app from ${versionStatus.localVersion} to ${versionStatus.storeVersion}', ); final dismissText = Text(this.dismissText); - final dismissAction = this.dismissAction ?? - () => Navigator.of(context, rootNavigator: true).pop(); + final dismissAction = + this.dismissAction ?? () => Navigator.of(context, rootNavigator: true).pop(); final updateText = Text(this.updateText); final updateAction = () { - _launchAppStore(versionStatus.appStoreLink); + _launchAppStore(versionStatus.appStoreLink!); Navigator.of(context, rootNavigator: true).pop(); }; final platform = Theme.of(context).platform; @@ -206,7 +212,7 @@ class NewVersion { /// Launches the Apple App Store or Google Play Store page for the app. void _launchAppStore(String appStoreLink) async { - print(appStoreLink); + debugPrint(appStoreLink); if (await canLaunch(appStoreLink)) { await launch(appStoreLink); } else { diff --git a/pubspec.yaml b/pubspec.yaml index 52854a4..48ff182 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,13 +4,13 @@ version: 0.0.7 homepage: https://github.com/timtraversy/new_version environment: - sdk: ">=2.0.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: flutter: sdk: flutter package_info_plus: ^1.0.0 - http: ^0.13.0 + http: ^0.13.1 html: ^0.15.0 url_launcher: ^6.0.2 From 607b2fea7deb1c0cbe51382cbc2dd9e333083f7a Mon Sep 17 00:00:00 2001 From: Viktor Date: Tue, 23 Mar 2021 16:02:03 +0100 Subject: [PATCH 2/7] version update --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 48ff182..93e61ca 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: new_version description: Check if your user has the most recent version of your Flutter app. -version: 0.0.7 +version: 0.0.8 homepage: https://github.com/timtraversy/new_version environment: From f76612fae42a407917491fd21bc864f6b0369740 Mon Sep 17 00:00:00 2001 From: Tim Traversy Date: Tue, 23 Mar 2021 12:33:27 -0400 Subject: [PATCH 3/7] Dart format file --- lib/new_version.dart | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/new_version.dart b/lib/new_version.dart index 2cabb0b..4a27936 100644 --- a/lib/new_version.dart +++ b/lib/new_version.dart @@ -103,7 +103,8 @@ class NewVersion { /// way. Future getVersionStatus() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); - VersionStatus versionStatus = VersionStatus(localVersion: packageInfo.version); + VersionStatus versionStatus = + VersionStatus(localVersion: packageInfo.version); switch (Theme.of(context).platform) { case TargetPlatform.android: final id = androidId ?? packageInfo.packageName; @@ -114,7 +115,8 @@ class NewVersion { versionStatus = await _getiOSStoreVersion(id, versionStatus); break; default: - debugPrint('This target platform is not yet supported by this package.'); + debugPrint( + 'This target platform is not yet supported by this package.'); } return versionStatus; @@ -141,7 +143,8 @@ class NewVersion { /// Android info is fetched by parsing the html of the app store page. _getAndroidStoreVersion(String id, VersionStatus versionStatus) async { - final uri = Uri.https("play.google.com", "/store/apps/details", {"id": "$id"}); + final uri = + Uri.https("play.google.com", "/store/apps/details", {"id": "$id"}); final response = await http.get(uri); if (response.statusCode != 200) { debugPrint('Can\'t find an app in the Play Store with the id: $id'); @@ -166,8 +169,8 @@ class NewVersion { 'You can now update this app from ${versionStatus.localVersion} to ${versionStatus.storeVersion}', ); final dismissText = Text(this.dismissText); - final dismissAction = - this.dismissAction ?? () => Navigator.of(context, rootNavigator: true).pop(); + final dismissAction = this.dismissAction ?? + () => Navigator.of(context, rootNavigator: true).pop(); final updateText = Text(this.updateText); final updateAction = () { _launchAppStore(versionStatus.appStoreLink!); From e8581eabf06ac54d1d759f2a02e17bafb0ef7bfe Mon Sep 17 00:00:00 2001 From: Tim Traversy Date: Tue, 23 Mar 2021 13:13:50 -0400 Subject: [PATCH 4/7] Make VersionStatus insubstantiable, simplify canUpdate computation --- lib/new_version.dart | 77 +++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/lib/new_version.dart b/lib/new_version.dart index 4a27936..0708994 100644 --- a/lib/new_version.dart +++ b/lib/new_version.dart @@ -2,6 +2,7 @@ library new_version; import 'dart:async'; import 'dart:convert'; +import 'dart:io' show Platform; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -14,30 +15,22 @@ import 'package:url_launcher/url_launcher.dart'; /// available in the Apple App Store or Google Play Store. class VersionStatus { /// True if the there is a more recent version of the app in the store. - bool get canUpdate { - if (storeVersion == null) { - return false; - } - if (localVersion != null) { - final current = int.tryParse(localVersion!.replaceAll('.', '')); - final store = int.tryParse(storeVersion!.replaceAll('.', '')); - if (current != null && store != null) { - return store > current; - } - } - return storeVersion != localVersion; - } + bool get canUpdate => localVersion.compareTo(storeVersion).isNegative; /// The current version of the app. - String? localVersion; + final String localVersion; /// The most recent version of the app in the store. - String? storeVersion; + final String storeVersion; /// A link to the app store page where the app can be updated. - String? appStoreLink; + final String appStoreLink; - VersionStatus({this.localVersion, this.storeVersion}); + VersionStatus._({ + required this.localVersion, + required this.storeVersion, + required this.appStoreLink, + }); } class NewVersion { @@ -78,9 +71,9 @@ class NewVersion { final String? iOSAppStoreCountry; NewVersion({ + required this.context, this.androidId, this.iOSId, - required this.context, this.dismissAction, this.dismissText: 'Maybe Later', this.updateText: 'Update', @@ -103,28 +96,20 @@ class NewVersion { /// way. Future getVersionStatus() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); - VersionStatus versionStatus = - VersionStatus(localVersion: packageInfo.version); - switch (Theme.of(context).platform) { - case TargetPlatform.android: - final id = androidId ?? packageInfo.packageName; - versionStatus = await _getAndroidStoreVersion(id, versionStatus); - break; - case TargetPlatform.iOS: - final id = iOSId ?? packageInfo.packageName; - versionStatus = await _getiOSStoreVersion(id, versionStatus); - break; - default: - debugPrint( - 'This target platform is not yet supported by this package.'); + if (Platform.isIOS) { + return _getiOSStoreVersion(packageInfo); + } else if (Platform.isAndroid) { + return _getAndroidStoreVersion(packageInfo); + } else { + debugPrint( + 'The target platform "${Platform.operatingSystem}" is not yet supported by this package.'); } - - return versionStatus; } /// iOS info is fetched by using the iTunes lookup API, which returns a /// JSON document. - _getiOSStoreVersion(String id, VersionStatus versionStatus) async { + Future _getiOSStoreVersion(PackageInfo packageInfo) async { + final id = iOSId ?? packageInfo.packageName; final parameters = {"bundleId": "$id"}; if (iOSAppStoreCountry != null) { parameters.addAll({"country": iOSAppStoreCountry!}); @@ -136,13 +121,17 @@ class NewVersion { return null; } final jsonObj = json.decode(response.body); - versionStatus.storeVersion = jsonObj['results'][0]['version']; - versionStatus.appStoreLink = jsonObj['results'][0]['trackViewUrl']; - return versionStatus; + return VersionStatus._( + localVersion: packageInfo.version, + storeVersion: jsonObj['results'][0]['version'], + appStoreLink: jsonObj['results'][0]['trackViewUrl'], + ); } /// Android info is fetched by parsing the html of the app store page. - _getAndroidStoreVersion(String id, VersionStatus versionStatus) async { + Future _getAndroidStoreVersion( + PackageInfo packageInfo) async { + final id = androidId ?? packageInfo.packageName; final uri = Uri.https("play.google.com", "/store/apps/details", {"id": "$id"}); final response = await http.get(uri); @@ -155,9 +144,11 @@ class NewVersion { final versionElement = elements.firstWhere( (elm) => elm.querySelector('.BgcNfc')!.text == 'Current Version', ); - versionStatus.storeVersion = versionElement.querySelector('.htlgb')!.text; - versionStatus.appStoreLink = uri.toString(); - return versionStatus; + return VersionStatus._( + localVersion: packageInfo.version, + storeVersion: versionElement.querySelector('.htlgb')!.text, + appStoreLink: uri.toString(), + ); } /// Shows the user a platform-specific alert about the app update. The user @@ -173,7 +164,7 @@ class NewVersion { () => Navigator.of(context, rootNavigator: true).pop(); final updateText = Text(this.updateText); final updateAction = () { - _launchAppStore(versionStatus.appStoreLink!); + _launchAppStore(versionStatus.appStoreLink); Navigator.of(context, rootNavigator: true).pop(); }; final platform = Theme.of(context).platform; From ae49cfe49fc19724843826bac9cc9a88175f30f7 Mon Sep 17 00:00:00 2001 From: Tim Traversy Date: Tue, 23 Mar 2021 13:23:25 -0400 Subject: [PATCH 5/7] Update CHANGELOG.md --- CHANGELOG.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dce732f..be4193b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,4 +16,15 @@ * Bump packages to latest version * Migrate from `package_info` to `package_info_plus` * Clean up example project -* Document all parameters in README \ No newline at end of file +* Document all parameters in README + +## 0.1.0 - 3/23/21 + +#### Breaking changes + +* `VersionStatus` can no longer be directly instantiated by the user. +* Migrated to null-safety, so certain fields are now `final` or non-null, which may break your code. + +#### Non-breaking changes + +* `canUpdate` now checks that the local version is *smaller* than the store version, not just that it is unequal. \ No newline at end of file From e597e5a1920a1792d6a3c27773497d47ab88b350 Mon Sep 17 00:00:00 2001 From: Tim Traversy Date: Tue, 23 Mar 2021 13:25:54 -0400 Subject: [PATCH 6/7] My changelog was upside-down this whole time --- CHANGELOG.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be4193b..97f2626 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,13 @@ -## 0.0.3 - 1/13/19 - -* Initial release. API includes `getVersionStatus` and `showAlertIfNecessary` methods. Support for iOS and Android. +## 0.1.0 - 3/23/21 -## 0.0.5 - 9/29/20 +#### Breaking changes -* Fix HTTPS bug on iOS. Fix null return for android version statuses. Upgrade dependencies. +* `VersionStatus` can no longer be directly instantiated by the user. +* Migrated to null-safety, so certain fields are now `final` or non-null, which may break your code. -## 0.0.6 - 1/15/21 +#### Non-breaking changes -* Add more granular parameters, fix Android web view and navigation bugs. +* `canUpdate` now checks that the local version is *smaller* than the store version, not just that it is unequal. ## 0.0.7 - 3/16/21 @@ -18,13 +17,14 @@ * Clean up example project * Document all parameters in README -## 0.1.0 - 3/23/21 +## 0.0.6 - 1/15/21 -#### Breaking changes +* Add more granular parameters, fix Android web view and navigation bugs. -* `VersionStatus` can no longer be directly instantiated by the user. -* Migrated to null-safety, so certain fields are now `final` or non-null, which may break your code. +## 0.0.5 - 9/29/20 -#### Non-breaking changes +* Fix HTTPS bug on iOS. Fix null return for android version statuses. Upgrade dependencies. + +## 0.0.3 - 1/13/19 -* `canUpdate` now checks that the local version is *smaller* than the store version, not just that it is unequal. \ No newline at end of file +* Initial release. API includes `getVersionStatus` and `showAlertIfNecessary` methods. Support for iOS and Android. \ No newline at end of file From 9e460b20fd4b9a1ca2ea10654d2f1d0638652083 Mon Sep 17 00:00:00 2001 From: Tim Traversy Date: Tue, 23 Mar 2021 13:41:26 -0400 Subject: [PATCH 7/7] Bump pubspec.yaml to 0.1.0 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 93e61ca..65f24c1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: new_version description: Check if your user has the most recent version of your Flutter app. -version: 0.0.8 +version: 0.1.0 homepage: https://github.com/timtraversy/new_version environment: