diff --git a/apps/chanora_flutter/lib/main.dart b/apps/chanora_flutter/lib/main.dart index a5e376f..7fa5867 100644 --- a/apps/chanora_flutter/lib/main.dart +++ b/apps/chanora_flutter/lib/main.dart @@ -16,6 +16,7 @@ import 'dart:io' show Platform; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; import 'l10n/generated/app_localizations.dart'; @@ -35,20 +36,49 @@ bool get _isTouchOnlyPttHost { return Platform.isIOS || Platform.isAndroid; } -/// Public version string shown in the About dialog. Aligned with -/// `pubspec.yaml` and the git tag for the MVP release candidate. -/// Bumped to rc.8 for the post-rc.7 v1 audio + PTT lifecycle work -/// (SDD-094..097, DEC-029..031). -const String _kAppVersion = 'v1.0.0-rc.8'; +/// Public version string shown in the About dialog. Resolved at +/// app init from `package_info_plus`, which reads the platform- +/// canonical version (iOS `CFBundleShortVersionString` + `CFBundleVersion`, +/// Android `versionName` + `versionCode`). Flutter populates both +/// from the SAME `pubspec.yaml` `version:` field, so whatever the +/// user sees in the About dialog is guaranteed to match the +/// commit that produced the build they're running. The "+build" +/// suffix is the post-`+` portion of the pubspec version and +/// distinguishes successive test builds during the iOS audio +/// rollout (every test commit bumps the build number). +/// +/// Initialised before `runApp` in `main()`. Falls back to the +/// hardcoded baseline if `PackageInfo.fromPlatform()` fails +/// (extremely unlikely — the platform channel call is a simple +/// constant lookup). +String _kAppVersion = 'v1.0.0-rc.8'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); await RustLib.init(); + await _resolveAppVersion(); unawaited(_wireStorage()); unawaited(_wireConnectivity()); runApp(const ChanoraApp()); } +/// Populate `_kAppVersion` from the platform manifest. Format is +/// `v+` (e.g. `v1.0.0-rc.8+60`). The `+` is +/// the iOS `CFBundleVersion` / Android `versionCode`, kept in +/// sync with `pubspec.yaml`'s `version: +`. +Future _resolveAppVersion() async { + try { + final info = await PackageInfo.fromPlatform(); + // info.version = "1.0.0-rc.8" (CFBundleShortVersionString) + // info.buildNumber = "60" (CFBundleVersion) + final build = info.buildNumber.isEmpty ? '' : '+${info.buildNumber}'; + _kAppVersion = 'v${info.version}$build'; + } catch (_) { + // Keep the hardcoded fallback. Already-correct for the + // semver portion; only the build counter is lost. + } +} + Future _wireStorage() async { try { final dir = await getApplicationSupportDirectory(); diff --git a/apps/chanora_flutter/pubspec.lock b/apps/chanora_flutter/pubspec.lock index ab7a1bc..62e69ac 100644 --- a/apps/chanora_flutter/pubspec.lock +++ b/apps/chanora_flutter/pubspec.lock @@ -309,6 +309,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.3" + http: + dependency: transitive + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" http_multi_server: dependency: transitive description: @@ -469,6 +477,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "16eee997588c60225bda0488b6dcfac69280a6b7a3cf02c741895dd370a02968" + url: "https://pub.dev" + source: hosted + version: "8.3.1" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" + url: "https://pub.dev" + source: hosted + version: "3.2.1" path: dependency: transitive description: @@ -730,6 +754,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + win32: + dependency: transitive + description: + name: win32 + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e + url: "https://pub.dev" + source: hosted + version: "5.15.0" xdg_directories: dependency: transitive description: diff --git a/apps/chanora_flutter/pubspec.yaml b/apps/chanora_flutter/pubspec.yaml index cfdd87f..2c30fbc 100644 --- a/apps/chanora_flutter/pubspec.yaml +++ b/apps/chanora_flutter/pubspec.yaml @@ -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 # 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. -version: 1.0.0-rc.8+59 +version: 1.0.0-rc.8+60 environment: sdk: ^3.11.5 @@ -63,6 +63,17 @@ dependencies: # wrong UI: only lists AirPlay output destinations, not the # speaker/receiver/Bluetooth choices we actually want. audio_session: ^0.2.3 + # package_info_plus 8.x (Flutter Community Plus, BSD-3, ~3M + # downloads) exposes the platform-canonical app version + build + # number at runtime so the About dialog (and any future + # diagnostic export) can display "v1.0.0-rc.8+60" sourced from + # the SAME pubspec.yaml field that drives CFBundleShortVersionString + # + CFBundleVersion on iOS and versionName + versionCode on + # Android. Single source of truth means the version on the + # iPhone screen always matches the commit that built it; no + # more "am I testing the right build?" question during the + # iOS audio test cycle. + package_info_plus: ^8.0.0 dev_dependencies: flutter_test: