|
1 | 1 | import 'dart:async'; |
| 2 | +import 'dart:io'; |
2 | 3 |
|
3 | 4 | import 'package:flutter/foundation.dart'; |
4 | 5 | import 'package:flutter/material.dart'; |
@@ -299,103 +300,125 @@ class _NfcReadingScreenState extends ConsumerState<NfcReadingScreen> with RouteA |
299 | 300 |
|
300 | 301 | _UiState passportReadingStateToUiState(PassportReaderState state) { |
301 | 302 | final progress = progressForState(state); |
| 303 | + final stateKey = _getTranslationKeyForState(state); |
| 304 | + |
302 | 305 | return switch (state) { |
303 | 306 | PassportReaderPending() => _UiState( |
304 | 307 | progress: progress, |
305 | | - stateKey: 'passport.nfc.connecting', |
| 308 | + stateKey: stateKey, |
306 | 309 | tipKey: 'passport.nfc.hold_near_photo_page', |
307 | 310 | ), |
308 | 311 | PassportReaderConnecting() => _UiState( |
309 | 312 | progress: progress, |
310 | | - stateKey: 'passport.nfc.connecting', |
| 313 | + stateKey: stateKey, |
311 | 314 | tipKey: 'passport.nfc.tip_2', |
312 | 315 | ), |
313 | 316 | PassportReaderAuthenticating() => _UiState( |
314 | 317 | progress: progress, |
315 | | - stateKey: 'passport.nfc.connecting', |
| 318 | + stateKey: stateKey, |
316 | 319 | tipKey: 'passport.nfc.tip_2', |
317 | 320 | ), |
318 | 321 | PassportReaderReadingCOM() => _UiState( |
319 | 322 | progress: progress, |
320 | | - stateKey: 'passport.nfc.reading_passport_data', |
| 323 | + stateKey: stateKey, |
321 | 324 | tipKey: 'passport.nfc.tip_3', |
322 | 325 | ), |
323 | 326 | PassportReaderReadingCardAccess() => _UiState( |
324 | 327 | progress: progress, |
325 | | - stateKey: 'passport.nfc.reading_card_security', |
| 328 | + stateKey: stateKey, |
326 | 329 | tipKey: 'passport.nfc.tip_3', |
327 | 330 | ), |
328 | 331 | PassportReaderReadingDataGroup() => _UiState( |
329 | 332 | progress: progress, |
330 | | - stateKey: 'passport.nfc.reading_passport_data', |
| 333 | + stateKey: stateKey, |
331 | 334 | tipKey: 'passport.nfc.tip_1', |
332 | 335 | ), |
333 | 336 | PassportReaderReadingSOD() => _UiState( |
334 | 337 | progress: progress, |
335 | | - stateKey: 'passport.nfc.reading_passport_data', |
| 338 | + stateKey: stateKey, |
336 | 339 | tipKey: 'passport.nfc.tip_2', |
337 | 340 | ), |
338 | 341 | PassportReaderActiveAuthentication() => _UiState( |
339 | 342 | progress: progress, |
340 | | - stateKey: 'passport.nfc.performing_security_verification', |
| 343 | + stateKey: stateKey, |
341 | 344 | tipKey: 'passport.nfc.tip_1', |
342 | 345 | ), |
343 | 346 | PassportReaderSuccess() => _UiState( |
344 | 347 | progress: progress, |
345 | | - stateKey: 'passport.nfc.success', |
| 348 | + stateKey: stateKey, |
346 | 349 | tipKey: 'passport.nfc.success_explanation', |
347 | 350 | ), |
348 | 351 | PassportReaderFailed(:final error) => _UiState( |
349 | 352 | progress: progress, |
350 | | - stateKey: 'passport.nfc.error', |
| 353 | + stateKey: stateKey, |
351 | 354 | tipKey: _readingErrorToHintKey(error), |
352 | 355 | ), |
353 | 356 | PassportReaderCancelling() => _UiState( |
354 | 357 | progress: progress, |
355 | | - stateKey: 'passport.nfc.cancelled', |
| 358 | + stateKey: stateKey, |
356 | 359 | tipKey: 'passport.nfc.cancelled_by_user', |
357 | 360 | ), |
358 | 361 | PassportReaderCancelled() => _UiState( |
359 | 362 | progress: progress, |
360 | | - stateKey: 'passport.nfc.cancelled', |
| 363 | + stateKey: stateKey, |
361 | 364 | tipKey: 'passport.nfc.cancelled_by_user', |
362 | 365 | ), |
363 | 366 | _ => throw Exception('unexpected state: $state'), |
364 | 367 | }; |
365 | 368 | } |
366 | 369 |
|
| 370 | + String _getTranslationKeyForState(PassportReaderState state) { |
| 371 | + return switch (state) { |
| 372 | + PassportReaderPending() => 'passport.nfc.hold_near_photo_page', |
| 373 | + PassportReaderCancelled() => 'passport.nfc.cancelled', |
| 374 | + PassportReaderCancelling() => 'passport.nfc.cancelling', |
| 375 | + PassportReaderFailed() => 'passport.nfc.error', |
| 376 | + PassportReaderConnecting() => 'passport.nfc.connecting', |
| 377 | + PassportReaderReadingCardAccess() => 'passport.nfc.reading_card_security', |
| 378 | + PassportReaderReadingCOM() => 'passport.nfc.reading_passport_data', |
| 379 | + PassportReaderAuthenticating() => 'passport.nfc.authenticating', |
| 380 | + PassportReaderReadingDataGroup() => 'passport.nfc.reading_passport_data', |
| 381 | + PassportReaderReadingSOD() => 'passport.nfc.reading_passport_data', |
| 382 | + PassportReaderActiveAuthentication() => 'passport.nfc.performing_security_verification', |
| 383 | + PassportReaderSuccess() => 'passport.nfc.success', |
| 384 | + _ => '', |
| 385 | + }; |
| 386 | + } |
| 387 | + |
367 | 388 | IosNfcMessageMapper _createIosNfcMessageMapper() { |
368 | 389 | String progressFormatter(double progress) { |
369 | 390 | const numStages = 10; |
370 | 391 | final prog = (progress * numStages).toInt(); |
371 | 392 | return '🟢' * prog + '⚪️' * (numStages - prog); |
372 | 393 | } |
373 | 394 |
|
| 395 | + final ios16OrHigher = _isiOS26OrHigher(); |
| 396 | + |
374 | 397 | return (state) { |
375 | 398 | final progress = progressFormatter(progressForState(state)); |
376 | 399 |
|
377 | | - final message = switch (state) { |
378 | | - PassportReaderPending() => FlutterI18n.translate(context, 'passport.nfc.hold_near_photo_page'), |
379 | | - PassportReaderCancelled() => FlutterI18n.translate(context, 'passport.nfc.cancelled'), |
380 | | - PassportReaderCancelling() => FlutterI18n.translate(context, 'passport.nfc.cancelling'), |
381 | | - PassportReaderFailed() => FlutterI18n.translate(context, 'passport.nfc.error'), |
382 | | - PassportReaderConnecting() => FlutterI18n.translate(context, 'passport.nfc.connecting'), |
383 | | - PassportReaderReadingCardAccess() => FlutterI18n.translate(context, 'passport.nfc.reading_card_security'), |
384 | | - PassportReaderReadingCOM() => FlutterI18n.translate(context, 'passport.nfc.reading_passport_data'), |
385 | | - PassportReaderAuthenticating() => FlutterI18n.translate(context, 'passport.nfc.authenticating'), |
386 | | - PassportReaderReadingDataGroup() => FlutterI18n.translate(context, 'passport.nfc.reading_passport_data'), |
387 | | - PassportReaderReadingSOD() => FlutterI18n.translate(context, 'passport.nfc.reading_passport_data'), |
388 | | - PassportReaderActiveAuthentication() => |
389 | | - FlutterI18n.translate(context, 'passport.nfc.performing_security_verification'), |
390 | | - PassportReaderSuccess() => FlutterI18n.translate(context, 'passport.nfc.success_explanation'), |
391 | | - _ => '', |
392 | | - }; |
| 400 | + // on iOS 26 only one line is shown, so we'll use that for progress |
| 401 | + if (ios16OrHigher) { |
| 402 | + return progress; |
| 403 | + } |
393 | 404 |
|
| 405 | + // on lower iOS versions a second line can be shown, so we'll use that for showing a message |
| 406 | + final message = FlutterI18n.translate(context, _getTranslationKeyForState(state)); |
394 | 407 | return '$progress\n$message'; |
395 | 408 | }; |
396 | 409 | } |
397 | 410 | } |
398 | 411 |
|
| 412 | +bool _isiOS26OrHigher() { |
| 413 | + if (!Platform.isIOS) return false; |
| 414 | + |
| 415 | + final match = RegExp(r'iOS (\d+)(?:\.(\d+))?').firstMatch(Platform.operatingSystemVersion); |
| 416 | + if (match == null) return false; |
| 417 | + |
| 418 | + final major = int.tryParse(match.group(1) ?? '0') ?? 0; |
| 419 | + return major >= 26; // replace with 26 or whichever major version you want |
| 420 | +} |
| 421 | + |
399 | 422 | Future _showLogsDialog(BuildContext context, String logs) async { |
400 | 423 | return showDialog( |
401 | 424 | context: context, |
|
0 commit comments