Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# manUp

## [9.4.0]

- Allow custom background duration check timeout

## [9.3.2]

- Launch url in external browser
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ Wrap your widget with `ManUpWidget` to automatically handle every thing.
shouldShowAlert: () => true,
onComplete: (bool isComplete) => print(isComplete),
onError: (dynamic e) => print(e.toString()),
checkAfterBackgroundDuration: Duration(minutes: 5), // Optional, only re-check after the app has been in the background for a certain time
child: Container()),
);
}
Expand Down
22 changes: 20 additions & 2 deletions lib/src/ui/man_up_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ class ManUpWidget extends StatefulWidget {
final void Function(dynamic e)? onError;
final void Function(ManUpStatus status)? onStatusChanged;

/// After the app has been backgrounded for this duration, check for updates again.
final Duration checkAfterBackgroundDuration;

@visibleForTesting
final DateTime Function() now;

ManUpWidget({
Key? key,
required this.child,
Expand All @@ -16,7 +22,10 @@ class ManUpWidget extends StatefulWidget {
this.onComplete,
this.onError,
this.onStatusChanged,
}) : super(key: key);
this.checkAfterBackgroundDuration = Duration.zero,
@visibleForTesting DateTime Function()? now,
}) : now = now ?? DateTime.now,
super(key: key);

@override
_ManUpWidgetState createState() => _ManUpWidgetState();
Expand All @@ -30,6 +39,8 @@ class _ManUpWidgetState extends State<ManUpWidget>
WidgetsBindingObserver {
ManUpStatus? alertDialogType;

DateTime? pausedAt;

@override
void initState() {
super.initState();
Expand Down Expand Up @@ -85,7 +96,14 @@ class _ManUpWidgetState extends State<ManUpWidget>
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
validateManUp();
final inBackgroundFor =
pausedAt?.difference(widget.now()).abs() ?? Duration.zero;
if (inBackgroundFor >= widget.checkAfterBackgroundDuration) {
validateManUp();
}
pausedAt = null;
} else if (state == AppLifecycleState.paused) {
pausedAt = widget.now();
}
}

Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: manup
description: Mandatory update for Flutter Apps that prompts or forces app update by querying a hosted JSON file.
version: 9.3.2
version: 9.4.0
homepage: https://github.com/NextFaze/flutter_manup

environment:
Expand Down
90 changes: 88 additions & 2 deletions test/man_up_widget_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ void main() {
const os = 'ios';

Future<MockManUpService> buildTestCase(WidgetTester tester,
{Metadata? metadata, String? version}) async {
{Metadata? metadata,
String? version,
Duration checkAfterBackgroundDuration = Duration.zero,
DateTime Function()? now}) async {
final manUpService = MockManUpService(
os: os,
packageInfoProvider:
Expand All @@ -17,7 +20,12 @@ void main() {
manUpService.metadata = metadata;
}
await tester.pumpWidget(MaterialApp(
home: ManUpWidget(child: Container(), service: manUpService),
home: ManUpWidget(
child: Container(),
service: manUpService,
checkAfterBackgroundDuration: checkAfterBackgroundDuration,
now: now,
),
));
return manUpService;
}
Expand Down Expand Up @@ -122,6 +130,77 @@ void main() {
tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.resumed);
await tester.pumpAndSettle();

expect(service.validateCallCount, 2);
expect(find.byType(AlertDialog), findsOneWidget);
expect(
find.text(
'The app is currently in maintenance, please check again shortly.'),
findsOneWidget);
});

testWidgets(
'skips resume re-check when background duration is below custom threshold',
(tester) async {
var now = DateTime(2020, 1, 1, 0, 0, 0);
final service = await buildTestCase(
tester,
checkAfterBackgroundDuration: Duration(minutes: 5),
now: () => now,
);

await tester.pumpAndSettle();
expect(service.validateCallCount, 1);
expect(find.byType(AlertDialog), findsNothing);

service.metadata = Metadata(data: {
os: {
"latest": "2.4.1",
"minimum": "2.1.0",
"url": "http://example.com/myAppUpdate",
"enabled": false
},
});

tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.paused);
await tester.pumpAndSettle();
now = now.add(Duration(minutes: 4));
tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.resumed);
await tester.pumpAndSettle();

expect(service.validateCallCount, 1);
expect(find.byType(AlertDialog), findsNothing);
});

testWidgets(
're-checks on resume when background duration meets custom threshold',
(tester) async {
var now = DateTime(2020, 1, 1, 0, 0, 0);
final service = await buildTestCase(
tester,
checkAfterBackgroundDuration: Duration(minutes: 5),
now: () => now,
);

await tester.pumpAndSettle();
expect(service.validateCallCount, 1);
expect(find.byType(AlertDialog), findsNothing);

service.metadata = Metadata(data: {
os: {
"latest": "2.4.1",
"minimum": "2.1.0",
"url": "http://example.com/myAppUpdate",
"enabled": false
},
});

tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.paused);
await tester.pumpAndSettle();
now = now.add(Duration(minutes: 6));
tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.resumed);
await tester.pumpAndSettle();

expect(service.validateCallCount, 2);
expect(find.byType(AlertDialog), findsOneWidget);
expect(
find.text(
Expand Down Expand Up @@ -188,6 +267,13 @@ class MockManUpService extends ManUpService {
MockManUpService({super.os, super.packageInfoProvider});

Metadata metadata = Metadata(data: {});
int validateCallCount = 0;

@override
Future<ManUpStatus> validate([Metadata? metadata]) {
validateCallCount += 1;
return super.validate(metadata);
}

@override
Future<Metadata> getMetadata() async => metadata;
Expand Down