Skip to content

Commit

Permalink
Added release notes. On iOS the release notes are automatically displ…
Browse files Browse the repository at this point in the history
…ayed. For Appcast the description will be used for release notes. Bumped version to 3.1.0.
  • Loading branch information
larryaasen committed Mar 15, 2021
1 parent 182c4f7 commit c1bde2b
Show file tree
Hide file tree
Showing 17 changed files with 212 additions and 67 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.1.0

* Added release notes. On iOS the release notes are automatically displayed. For Appcast the description will be used for release notes.

## 3.0.0

* Moved to Flutter 2.0.0 stable.
Expand Down
31 changes: 18 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@ popup alert prompt, and the [UpgradeCard](#card-example) class is used to displa

The text displayed in the upgrader package is localized in many languages, and supports customization.

The release notes are displayed by default when a new version is available. On iOS the release
notes are taken from the App Store What's New section. For [appcast](#appcast)), the
release notes are taken from the description field.

## Alert Example

Just wrap your body widget in the UpgradeAlert widget, and it will handle the rest.
Just wrap your body widget in the `UpgradeAlert` widget, and it will handle the rest.
```dart
import 'package:flutter/material.dart';
import 'package:upgrader/upgrader.dart';
Expand Down Expand Up @@ -60,7 +64,7 @@ class MyApp extends StatelessWidget {

## Cupertino Alert Example

You can also display a Cupertino style dialog by using the dialogStyle parameter.
You can also display a Cupertino style dialog by using the `dialogStyle` parameter.
```dart
body: UpgradeAlert(
dialogStyle: UpgradeDialogStyle.cupertino,
Expand All @@ -74,7 +78,7 @@ You can also display a Cupertino style dialog by using the dialogStyle parameter

## Card Example

Just return an UpgradeCard widget in your build method and a material design card will be displayed
Just return an `UpgradeCard` widget in your build method and a material design card will be displayed
when an update is detected. The widget will have width and height of 0.0 when no update is detected.
```dart
return Container(
Expand All @@ -101,8 +105,9 @@ UpgradeAlert widget.
* onIgnore: called when the ignore button is tapped, defaults to ```null```
* onLater: called when the later button is tapped, defaults to ```null```
* onUpdate: called when the update button is tapped, defaults to ```null```
* showIgnore: hide or show Ignore button on dialog, which defaults to ```true```
* showLater: hide or show Later button on dialog, which defaults to ```true```
* showIgnore: hide or show Ignore button, which defaults to ```true```
* showLater: hide or show Later button, which defaults to ```true```
* showReleaseNotes: hide or show release notes, which defaults to ```true```
* canDismissDialog: can alert dialog be dismissed on tap outside of the alert dialog, which defaults to ```false``` (not used by alert card)
* countryCode: the country code that will override the system locale, which defaults to ```null``` (iOS only)
* minAppVersion: the minimum app version supported by this app. Earlier versions of this app will be forced to update to the current version. Defaults to ```null```.
Expand All @@ -119,14 +124,13 @@ the app is in the `US` App Store.
These widgets work on both Android and iOS. When running on iOS the App Store will provide the
latest app version and will display the prompt at the appropriate times.

On Android, this widget
does nothing (unless using [appcast](#appcast)) as there is no easy way to query the Google Play Store for metadata about an app.
On Android, this widget normally does nothing unless the [appcast](#appcast)) is used.
There is no easy way to query the Google Play Store for metadata about an app.
Without the metadata, the widget cannot compare the app version with the latest Play Store version.
It will not disrupt the widget tree and can be
included in an Android without any issues.
It will not disrupt the widget tree and can be included in an Android app without any issues.

There is now an [appcast](#appcast) that can be used for Android and iOS to remotely configure the
latest app version.
There is an [appcast](#appcast) that can be used to remotely configure the
latest app version. See [appcast](#appcast) below for more details.

## Appcast

Expand Down Expand Up @@ -161,7 +165,7 @@ final bestItem = appcast.bestItem();
<title>Debt Now App - Appcast</title>
<item>
<title>Version 1.15.0</title>
<description>desc</description>
<description>Minor updates and improvements.</description>
<pubDate>Sun, 30 Dec 2018 12:00:00 +0000</pubDate>
<enclosure url="https://play.google.com/store/apps/details?id=com.moonwink.treasury" sparkle:version="1.15.0" sparkle:os="android" />
</item>
Expand Down Expand Up @@ -311,8 +315,9 @@ Results:
upgrader: download: https://itunes.apple.com/lookup?bundleId=com.google.Maps
upgrader: response statusCode: 200
itunes_lookup bundleId: com.google.Maps
itunes_lookup releaseNotes: Thanks for using Google Maps!
itunes_lookup trackViewUrl: https://apps.apple.com/us/app/google-maps-transit-food/id585027354?uo=4
itunes_lookup version: 5.31
itunes_lookup version: 5.58
itunes_lookup all results:
{resultCount: 1, results:
...
Expand Down
53 changes: 36 additions & 17 deletions bin/itunes_lookup.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Larry Aasen. All rights reserved.
* Copyright (c) 2019-2021 Larry Aasen. All rights reserved.
*/

/*
Expand All @@ -9,9 +9,10 @@

import 'package:upgrader/src/itunes_search_api.dart';

void main(List<String> arguments) {
void main(List<String> arguments) async {
final defaultLookupBundleId = 'com.google.Maps';
var lookupBundleId = defaultLookupBundleId;
var lookupAppId;

if (arguments.length == 1) {
final arg0 = arguments[0].split('=');
Expand All @@ -21,26 +22,44 @@ void main(List<String> arguments) {

if (argName == 'bundleid') {
lookupBundleId = argValue;
} else if (argName == 'appid') {
lookupAppId = argValue;
}
}
}

final iTunes = ITunesSearchAPI();
iTunes.debugEnabled = true;
final countryCode = 'US';
final resultsFuture = iTunes.lookupByBundleId(
lookupBundleId,
country: countryCode,
);
resultsFuture.then((results) {
final bundleId = ITunesResults.bundleId(results!);
final trackViewUrl = ITunesResults.trackViewUrl(results);
final version = ITunesResults.version(results);

print('itunes_lookup bundleId: $bundleId');
print('itunes_lookup trackViewUrl: $trackViewUrl');
print('itunes_lookup version: $version');

print('itunes_lookup all results:\n$results');
});

var results;
if (lookupAppId != null) {
results = await iTunes.lookupById(
lookupAppId,
country: countryCode,
);
} else {
results = await iTunes.lookupByBundleId(
lookupBundleId,
country: countryCode,
);
}

if (results == null) {
print('itunes_lookup there are no results');
return;
}

final bundleId = ITunesResults.bundleId(results);
final releaseNotes = ITunesResults.releaseNotes(results);
final trackViewUrl = ITunesResults.trackViewUrl(results);
final version = ITunesResults.version(results);

print('itunes_lookup bundleId: $bundleId');
print('itunes_lookup releaseNotes: $releaseNotes');
print('itunes_lookup trackViewUrl: $trackViewUrl');
print('itunes_lookup version: $version');

print('itunes_lookup all results:\n$results');
return;
}
2 changes: 1 addition & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Only call clearSavedSettings() during testing to reset internal values.
Upgrader().clearSavedSettings();
Upgrader().clearSavedSettings(); // Remove this for release builds

// On Android, setup the Appcast.
// On iOS, the default behavior will be to use the App Store version of
Expand Down
4 changes: 4 additions & 0 deletions lib/src/appcast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class Appcast {
String? maximumSystemVersion;
String? minimumSystemVersion;
String? osString;
String? releaseNotesLink;
final tags = <String>[];
String? newVersion;
String? itemVersion;
Expand Down Expand Up @@ -127,6 +128,8 @@ class Appcast {
minimumSystemVersion = childNode.text;
} else if (name == AppcastConstants.ElementPubDate) {
dateString = childNode.text;
} else if (name == AppcastConstants.ElementReleaseNotesLink) {
releaseNotesLink = childNode.text;
} else if (name == AppcastConstants.ElementTags) {
childNode.children.forEach((XmlNode tagChildNode) {
if (tagChildNode is XmlElement) {
Expand Down Expand Up @@ -158,6 +161,7 @@ class Appcast {
maximumSystemVersion: maximumSystemVersion,
minimumSystemVersion: minimumSystemVersion,
osString: osString,
releaseNotesURL: releaseNotesLink,
tags: tags,
fileURL: fileURL,
versionString: newVersion,
Expand Down
13 changes: 12 additions & 1 deletion lib/src/itunes_search_api.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018 Larry Aasen. All rights reserved.
* Copyright (c) 2018-2021 Larry Aasen. All rights reserved.
*/

import 'dart:async';
Expand Down Expand Up @@ -141,6 +141,17 @@ class ITunesResults {
return value;
}

/// Return field releaseNotes from iTunes results.
static String? releaseNotes(Map response) {
var value;
try {
value = response['results'][0]['releaseNotes'];
} catch (e) {
print('upgrader.ITunesResults.releaseNotes: $e');
}
return value;
}

/// Return field trackViewUrl from iTunes results.
static String? trackViewUrl(Map response) {
var value;
Expand Down
2 changes: 2 additions & 0 deletions lib/src/upgrade_alert.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class UpgradeAlert extends UpgradeBase {
http.Client? client,
bool? showIgnore,
bool? showLater,
bool? showReleaseNotes,
bool? canDismissDialog,
String? countryCode,
String? minAppVersion,
Expand All @@ -45,6 +46,7 @@ class UpgradeAlert extends UpgradeBase {
client: client,
showIgnore: showIgnore,
showLater: showLater,
showReleaseNotes: showReleaseNotes,
canDismissDialog: canDismissDialog,
countryCode: countryCode,
minAppVersion: minAppVersion,
Expand Down
7 changes: 7 additions & 0 deletions lib/src/upgrade_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ class UpgradeBase extends StatefulWidget {
/// Hide or show Later button on dialog (default: true)
final bool? showLater;

/// Hide or show release notes (default: true)
final bool? showReleaseNotes;

/// Can alert dialog be dismissed on tap outside of the alert dialog. Not used by alert card. (default: false)
final bool? canDismissDialog;

Expand Down Expand Up @@ -74,6 +77,7 @@ class UpgradeBase extends StatefulWidget {
this.client,
this.showIgnore,
this.showLater,
this.showReleaseNotes,
this.canDismissDialog,
this.countryCode,
this.minAppVersion,
Expand Down Expand Up @@ -115,6 +119,9 @@ class UpgradeBase extends StatefulWidget {
if (showLater != null) {
Upgrader().showLater = showLater!;
}
if (showReleaseNotes != null) {
Upgrader().showReleaseNotes = showReleaseNotes!;
}
if (canDismissDialog != null) {
Upgrader().canDismissDialog = canDismissDialog!;
}
Expand Down
62 changes: 48 additions & 14 deletions lib/src/upgrade_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class UpgradeCard extends UpgradeBase {
http.Client? client,
bool? showIgnore,
bool? showLater,
bool? showReleaseNotes,
bool? canDismissDialog,
String? countryCode,
String? minAppVersion,
Expand All @@ -47,6 +48,7 @@ class UpgradeCard extends UpgradeBase {
client: client,
showIgnore: showIgnore,
showLater: showLater,
showReleaseNotes: showReleaseNotes,
canDismissDialog: canDismissDialog,
countryCode: countryCode,
minAppVersion: minAppVersion,
Expand All @@ -64,34 +66,66 @@ class UpgradeCard extends UpgradeBase {
if (processed.connectionState == ConnectionState.done) {
assert(Upgrader().messages != null);
if (Upgrader().shouldDisplayUpgrade()) {
final title = Upgrader().messages!.message(UpgraderMessage.title);
final message = Upgrader().message();
final releaseNotes = Upgrader().releaseNotes;
final shouldDisplayReleaseNotes =
Upgrader().shouldDisplayReleaseNotes();
if (Upgrader().debugLogging) {
print('UpgradeCard: will display');
print('UpgradeCard: showDialog title: $title');
print('UpgradeCard: showDialog message: $message');
print(
'UpgradeCard: shouldDisplayReleaseNotes: ${shouldDisplayReleaseNotes}');

print('UpgradeCard: showDialog releaseNotes: $releaseNotes');
}

Widget? notes;
if (shouldDisplayReleaseNotes && releaseNotes != null) {
notes = Padding(
padding: EdgeInsets.only(top: 15.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Release Notes:',
style: TextStyle(fontWeight: FontWeight.bold)),
Text(
releaseNotes,
maxLines: 15,
overflow: TextOverflow.ellipsis,
),
],
));
}

return Card(
color: Colors.white,
margin: margin,
child: AlertStyleWidget(
title: Text(
Upgrader().messages!.message(UpgraderMessage.title)!),
title: Text(title ?? ''),
content: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(Upgrader().message()),
Text(message),
Padding(
padding: EdgeInsets.only(top: 15.0),
child: Text(Upgrader()
.messages!
.message(UpgraderMessage.prompt)!)),
.messages!
.message(UpgraderMessage.prompt) ??
'')),
if (notes != null) notes,
],
),
actions: <Widget>[
if (Upgrader().showIgnore)
TextButton(
child: Text(Upgrader()
.messages!
.message(UpgraderMessage.buttonTitleIgnore)!),
child: Text(Upgrader().messages!.message(
UpgraderMessage.buttonTitleIgnore) ??
''),
onPressed: () {
// Save the date/time as the last time alerted.
Upgrader().saveLastAlerted();
Expand All @@ -101,9 +135,9 @@ class UpgradeCard extends UpgradeBase {
}),
if (Upgrader().showLater)
TextButton(
child: Text(Upgrader()
.messages!
.message(UpgraderMessage.buttonTitleLater)!),
child: Text(Upgrader().messages!.message(
UpgraderMessage.buttonTitleLater) ??
''),
onPressed: () {
// Save the date/time as the last time alerted.
Upgrader().saveLastAlerted();
Expand All @@ -112,9 +146,9 @@ class UpgradeCard extends UpgradeBase {
state.forceUpdateState();
}),
TextButton(
child: Text(Upgrader()
.messages!
.message(UpgraderMessage.buttonTitleUpdate)!),
child: Text(Upgrader().messages!.message(
UpgraderMessage.buttonTitleUpdate) ??
''),
onPressed: () {
// Save the date/time as the last time alerted.
Upgrader().saveLastAlerted();
Expand Down
Loading

0 comments on commit c1bde2b

Please sign in to comment.