checkVersion method

Future<void> checkVersion()

Checks for a newer release and updates state.

This is the strict low-level check: failures move state to UpdateFailed and are rethrown to the caller. Use checkForUpdates for user-triggered checks that should return a typed result instead.

Implementation

Future<void> checkVersion() async {
  final archiveUrl = _appArchiveUrl;
  if (archiveUrl == null) {
    throw StateError("App archive URL is not set.");
  }

  _diagnosticsRecorder
    ..clear()
    ..record(
      stage: UpdateDiagnosticStage.check,
      level: UpdateDiagnosticLevel.info,
      message: "Checking for updates from $archiveUrl",
    );
  _lastCleanupReport = null;
  _clearReleaseNotesCache();
  _state = const UpdateChecking();
  emitUpdateTelemetry(
    telemetry,
    UpdateTelemetryEvent.checkStarted(
      source: archiveUrl,
      channel: channel,
    ),
  );
  notifyListeners();

  try {
    final currentVersion = await currentVersionInfo();
    if (currentVersion == null) {
      throw StateError("Current app version is unavailable.");
    }
    _currentAppVersion = _formatVersionInfo(currentVersion);

    final client = UpdateClient(
      appArchiveUrl: archiveUrl,
      currentVersion: currentVersion,
      channel: channel,
      installationIdentity: installationIdentity,
      telemetry: telemetry,
      isMinimumOSSupported: isMinimumOSSupported,
    );
    final result = await client.checkForUpdate();
    if (result == null) {
      _client = null;
      _activeDescriptor = null;
      _stagingPath = null;
      _skipUpdate = false;
      _clearReleaseNotesCache();
      _diagnosticsRecorder.record(
        stage: UpdateDiagnosticStage.check,
        level: UpdateDiagnosticLevel.info,
        message: "No update is available.",
      );
      _state = const UpdateIdle();
      notifyListeners();
      return;
    }

    if (!result.item.mandatory && await _isSkipped(result.descriptor)) {
      _client = null;
      _activeDescriptor = null;
      _stagingPath = null;
      _skipUpdate = true;
      _clearReleaseNotesCache();
      _diagnosticsRecorder.record(
        stage: UpdateDiagnosticStage.policy,
        level: UpdateDiagnosticLevel.info,
        message: "Update ${result.descriptor.version} is skipped.",
      );
      _state = const UpdateIdle();
      notifyListeners();
      return;
    }

    _skipUpdate = false;
    _client = client;
    _activeDescriptor = result.descriptor;
    _stagingPath = null;
    _diagnosticsRecorder.record(
      stage: UpdateDiagnosticStage.descriptor,
      level: UpdateDiagnosticLevel.info,
      message: "Update selected: ${result.descriptor.version} "
          "(${result.descriptor.platform}/${result.descriptor.channel}).",
    );
    _state = UpdateAvailable(
      descriptor: result.descriptor,
      mandatory: result.item.mandatory,
    );
    emitUpdateTelemetry(
      telemetry,
      UpdateTelemetryEvent.updateSelected(
        version: result.descriptor.version,
        channel: result.descriptor.channel,
        platform: result.descriptor.platform,
        mandatory: result.item.mandatory,
      ),
    );
    notifyListeners();
  } on Object catch (error) {
    _diagnosticsRecorder.record(
      stage: UpdateDiagnosticStage.check,
      level: UpdateDiagnosticLevel.error,
      message: "Update check failed.",
      error: error,
    );
    _state = UpdateFailed(error, report: _buildProblemReport(error));
    emitUpdateTelemetry(
      telemetry,
      UpdateTelemetryEvent.checkFailed(
        source: archiveUrl,
        channel: channel,
        error: error,
      ),
    );
    notifyListeners();
    rethrow;
  }
}