Skip to content

Commit 344ca12

Browse files
authored
feat: prompting users to upgrade when newer app version available (#1061)
* feat: add in-app update check using upgrader Integrate the `upgrader` package to prompt users to update the app when a new version is available. Wrap the main application widget with `UpgradeAlert` to enable the update check functionality. * feat: add intent filters for URL handling Allow the app to handle HTTPS URLs with specified actions and categories by adding intent filters in the AndroidManifest.xml. * feat: display app version in the drawer Add version and build number display in the app drawer using the `package_info_plus` package. Include dynamic rendering through `FutureBuilder`. * feat: customize "Update now" behavior with UpgradeDialog Replace `UpgradeAlert` with a new `UpgradeDialog` widget to customize the "Update now" button functionality. Redirect users to the appropriate app store or GitHub Releases page based on the installer source. * feat: refine "Update now" behavior on Android Adjust the "Update now" functionality to handle updates differently for Android by introducing a custom URL resolution method and preventing default behavior when the update action is handled. * Update iOS CI workflow to use macOS 14 runner * Add `--break-system-packages` flag and remove redundant pip upgrade in iOS CI post-clone script. * Set up iPhone 16 Pro simulator in iOS CI workflow * Fix defaults command in iOS CI simulator setup script to correctly set `CurrentDeviceUDID` and add fallback warning * Improve backend description layout in BenchmarkConfigSection to support text overflow handling
1 parent e52399d commit 344ca12

File tree

6 files changed

+180
-1
lines changed

6 files changed

+180
-1
lines changed

flutter/android/app/src/main/AndroidManifest.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,11 @@
7272
<meta-data android:name="flutterEmbedding"
7373
android:value="2"/>
7474
</application>
75+
<queries>
76+
<intent>
77+
<action android:name="android.intent.action.VIEW" />
78+
<category android:name="android.intent.category.BROWSABLE" />
79+
<data android:scheme="https" />
80+
</intent>
81+
</queries>
7582
</manifest>

flutter/lib/ui/home/app_drawer.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:flutter/material.dart';
22

3+
import 'package:package_info_plus/package_info_plus.dart';
34
import 'package:url_launcher/url_launcher.dart';
45

56
import 'package:mlperfbench/app_constants.dart';
@@ -160,6 +161,34 @@ Widget buildFooter(BuildContext context) {
160161
title: Text(l10n.settingsEula),
161162
onTap: () => launchUrl(Uri.parse(Url.eula)),
162163
),
164+
FutureBuilder<PackageInfo>(
165+
future: PackageInfo.fromPlatform(),
166+
builder: (context, snapshot) {
167+
if (snapshot.connectionState != ConnectionState.done) {
168+
return const SizedBox.shrink();
169+
}
170+
if (!snapshot.hasData) {
171+
return const SizedBox.shrink();
172+
}
173+
final info = snapshot.data!;
174+
final version = info.version;
175+
final build = info.buildNumber;
176+
var versionText = 'v$version';
177+
if (build.isNotEmpty) {
178+
versionText = '$versionText ($build)';
179+
}
180+
return Padding(
181+
padding: const EdgeInsets.all(2.0),
182+
child: Text(
183+
versionText,
184+
style: Theme.of(context).textTheme.bodySmall?.copyWith(
185+
color: AppColors.drawerForeground.withOpacity(0.6),
186+
),
187+
textAlign: TextAlign.center,
188+
),
189+
);
190+
},
191+
),
163192
],
164193
);
165194
}

flutter/lib/ui/root/app.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:bot_toast/bot_toast.dart';
44

55
import 'package:mlperfbench/localizations/app_localizations.dart';
66
import 'package:mlperfbench/ui/app_styles.dart';
7+
import 'package:mlperfbench/ui/root/upgrade_dialog.dart';
78

89
class MyApp extends StatelessWidget {
910
final Widget home;
@@ -40,7 +41,7 @@ class MyApp extends StatelessWidget {
4041
),
4142
builder: BotToastInit(),
4243
navigatorObservers: [BotToastNavigatorObserver()],
43-
home: home,
44+
home: UpgradeDialog(child: home),
4445
);
4546
}
4647
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import 'dart:io' show Platform;
2+
3+
import 'package:flutter/material.dart';
4+
5+
import 'package:package_info_plus/package_info_plus.dart';
6+
import 'package:upgrader/upgrader.dart';
7+
import 'package:url_launcher/url_launcher.dart';
8+
9+
/// A wrapper widget around [UpgradeAlert] that customizes the "Update now"
10+
/// button behavior. It redirects users to the appropriate store based on the
11+
/// installer source. If the installer is unknown (e.g., sideloaded APK), it
12+
/// opens the project's GitHub Releases page.
13+
class UpgradeDialog extends StatelessWidget {
14+
final Widget child;
15+
16+
const UpgradeDialog({super.key, required this.child});
17+
18+
@override
19+
Widget build(BuildContext context) {
20+
final upgrader = Upgrader(
21+
durationUntilAlertAgain: const Duration(hours: 48),
22+
);
23+
return UpgradeAlert(
24+
upgrader: upgrader,
25+
onUpdate: () {
26+
if (Platform.isAndroid) {
27+
_onUpdateAndroid();
28+
return false;
29+
} else {
30+
return true; // Return true to allow Upgrader's default behavior to proceed.
31+
}
32+
},
33+
child: child,
34+
);
35+
}
36+
37+
// Handler for the "Update now" button on Android
38+
Future<bool> _onUpdateAndroid() async {
39+
// For Android and other platforms, resolve a custom URL.
40+
final uri = await _resolveUpdateUri();
41+
if (uri == null) return true;
42+
final can = await canLaunchUrl(uri);
43+
if (can) {
44+
await launchUrl(uri, mode: LaunchMode.externalApplication);
45+
return false;
46+
}
47+
return true;
48+
}
49+
50+
// Decide which URL to open for the update action based on installer source.
51+
Future<Uri?> _resolveUpdateUri() async {
52+
try {
53+
final info = await PackageInfo.fromPlatform();
54+
final installer = info.installerStore;
55+
final packageName = info.packageName;
56+
if (Platform.isAndroid) {
57+
// Map known Android installer package names to their corresponding store URLs.
58+
final uri = _androidStoreUriFor(installer, packageName);
59+
if (uri != null) return uri;
60+
}
61+
// Fallback/update source for sideloaded APKs or unknown installers.
62+
// Use the public GitHub releases page for this project.
63+
return Uri.parse('https://github.com/mlcommons/mobile_app_open/releases');
64+
} catch (_) {
65+
// In case of any exception, fall back to the releases page.
66+
return Uri.parse('https://github.com/mlcommons/mobile_app_open/releases');
67+
}
68+
}
69+
70+
Uri? _androidStoreUriFor(String? installer, String packageName) {
71+
switch (installer) {
72+
// Google Play Store
73+
case 'com.android.vending':
74+
return Uri.parse(
75+
'https://play.google.com/store/apps/details?id=$packageName');
76+
// Amazon Appstore
77+
case 'com.amazon.venezia':
78+
return Uri.parse(
79+
'https://www.amazon.com/gp/mas/dl/android?p=$packageName');
80+
// Samsung Galaxy Store
81+
case 'com.sec.android.app.samsungapps':
82+
return Uri.parse(
83+
'https://apps.samsung.com/appquery/appDetail.as?appId=$packageName');
84+
// Huawei AppGallery (best-effort web link; may require app-specific ID)
85+
case 'com.huawei.appmarket':
86+
return Uri.parse('https://appgallery.huawei.com/#/app/$packageName');
87+
// Xiaomi GetApps
88+
case 'com.xiaomi.mipicks':
89+
return Uri.parse('https://global.app.mi.com/details?id=$packageName');
90+
// Oppo App Market
91+
case 'com.oppo.market':
92+
return Uri.parse(
93+
'https://store.heytap.com/app/details?id=$packageName');
94+
// Vivo App Store
95+
case 'com.bbk.appstore':
96+
return Uri.parse('https://appstore.vivo.com.cn/appdetail/$packageName');
97+
default:
98+
return null;
99+
}
100+
}
101+
}

flutter/pubspec.lock

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,14 @@ packages:
209209
url: "https://pub.dev"
210210
source: hosted
211211
version: "3.0.3"
212+
csslib:
213+
dependency: transitive
214+
description:
215+
name: csslib
216+
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
217+
url: "https://pub.dev"
218+
source: hosted
219+
version: "1.0.2"
212220
cupertino_icons:
213221
dependency: "direct main"
214222
description:
@@ -567,6 +575,14 @@ packages:
567575
url: "https://pub.dev"
568576
source: hosted
569577
version: "2.3.1"
578+
html:
579+
dependency: transitive
580+
description:
581+
name: html
582+
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
583+
url: "https://pub.dev"
584+
source: hosted
585+
version: "0.15.6"
570586
http:
571587
dependency: "direct main"
572588
description:
@@ -740,6 +756,14 @@ packages:
740756
url: "https://pub.dev"
741757
source: hosted
742758
version: "1.0.0"
759+
os_detect:
760+
dependency: transitive
761+
description:
762+
name: os_detect
763+
sha256: e704fb99aa30b2b9a284d87a28eef9ba262f68c25c963d5eb932f54cad07784f
764+
url: "https://pub.dev"
765+
source: hosted
766+
version: "2.0.2"
743767
package_config:
744768
dependency: transitive
745769
description:
@@ -1177,6 +1201,14 @@ packages:
11771201
url: "https://pub.dev"
11781202
source: hosted
11791203
version: "1.0.0+1"
1204+
upgrader:
1205+
dependency: "direct main"
1206+
description:
1207+
name: upgrader
1208+
sha256: d45483694620883107c2f5ca1dff7cdd4237b16810337a9c9c234203eb79eb5f
1209+
url: "https://pub.dev"
1210+
source: hosted
1211+
version: "10.3.0"
11801212
url_launcher:
11811213
dependency: "direct main"
11821214
description:
@@ -1281,6 +1313,14 @@ packages:
12811313
url: "https://pub.dev"
12821314
source: hosted
12831315
version: "2.1.4"
1316+
version:
1317+
dependency: transitive
1318+
description:
1319+
name: version
1320+
sha256: "3d4140128e6ea10d83da32fef2fa4003fccbf6852217bb854845802f04191f94"
1321+
url: "https://pub.dev"
1322+
source: hosted
1323+
version: "3.0.2"
12841324
vm_service:
12851325
dependency: transitive
12861326
description:

flutter/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ dependencies:
5252
firebase_ui_auth: ^1.14.0
5353
firebase_crashlytics: ^3.5.5
5454
firebase_app_check: ^0.2.2+6
55+
upgrader: ^10.3.0
5556

5657
dev_dependencies:
5758
flutter_test:

0 commit comments

Comments
 (0)