mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-22 17:51:29 +03:00
Merge PR #1577
This commit is contained in:
commit
b0010da643
6
NEWS
6
NEWS
@ -1,3 +1,9 @@
|
||||
* Version 7.0.1 (released 2024-05-30) Android only release
|
||||
** Fix: Opening the app by NFC tap needs another tap to reveal accounts.
|
||||
** Fix: NFC devices attached to mobile phone prevent usage of USB YubiKeys.
|
||||
** Fix: Invalid colors shown in customization views for Android Dynamic color.
|
||||
** Fix: Fingerprints are shown in random order.
|
||||
|
||||
* Version 7.0.0 (released 2024-05-06)
|
||||
** UI: Add home screen with device information, customization options, and factory reset.
|
||||
** UI: Add search filtering to Passkeys and display more information.
|
||||
|
@ -108,9 +108,13 @@ class MainActivity : FlutterFragmentActivity() {
|
||||
logger.debug("Starting nfc discovery")
|
||||
yubikit.startNfcDiscovery(
|
||||
nfcConfiguration.disableNfcDiscoverySound(appPreferences.silenceNfcSounds),
|
||||
this,
|
||||
::processYubiKey
|
||||
)
|
||||
this
|
||||
) { nfcYubiKeyDevice ->
|
||||
if (!deviceManager.isUsbKeyConnected()) {
|
||||
launchProcessYubiKey(nfcYubiKeyDevice)
|
||||
}
|
||||
}
|
||||
|
||||
hasNfc = true
|
||||
} catch (e: NfcNotAvailable) {
|
||||
hasNfc = false
|
||||
@ -131,7 +135,7 @@ class MainActivity : FlutterFragmentActivity() {
|
||||
logger.debug("YubiKey was disconnected, stopping usb discovery")
|
||||
stopUsbDiscovery()
|
||||
}
|
||||
processYubiKey(device)
|
||||
launchProcessYubiKey(device)
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,7 +218,7 @@ class MainActivity : FlutterFragmentActivity() {
|
||||
val device = NfcYubiKeyDevice(tag, nfcConfiguration.timeout, executor)
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
contextManager?.processYubiKey(device)
|
||||
processYubiKey(device)
|
||||
device.remove {
|
||||
executor.shutdown()
|
||||
startNfcDiscovery()
|
||||
@ -269,38 +273,42 @@ class MainActivity : FlutterFragmentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun processYubiKey(device: YubiKeyDevice) {
|
||||
private suspend fun processYubiKey(device: YubiKeyDevice) {
|
||||
val deviceInfo = getDeviceInfo(device)
|
||||
deviceManager.setDeviceInfo(deviceInfo)
|
||||
|
||||
if (deviceInfo == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val supportedContexts = DeviceManager.getSupportedContexts(deviceInfo)
|
||||
logger.debug("Connected key supports: {}", supportedContexts)
|
||||
if (!supportedContexts.contains(viewModel.appContext.value)) {
|
||||
val preferredContext = DeviceManager.getPreferredContext(supportedContexts)
|
||||
logger.debug(
|
||||
"Current context ({}) is not supported by the key. Using preferred context {}",
|
||||
viewModel.appContext.value,
|
||||
preferredContext
|
||||
)
|
||||
switchContext(preferredContext)
|
||||
}
|
||||
|
||||
if (contextManager == null) {
|
||||
switchContext(DeviceManager.getPreferredContext(supportedContexts))
|
||||
}
|
||||
|
||||
contextManager?.let {
|
||||
try {
|
||||
it.processYubiKey(device)
|
||||
} catch (e: Throwable) {
|
||||
logger.error("Error processing YubiKey in AppContextManager", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchProcessYubiKey(device: YubiKeyDevice) {
|
||||
lifecycleScope.launch {
|
||||
val deviceInfo = getDeviceInfo(device)
|
||||
deviceManager.setDeviceInfo(deviceInfo)
|
||||
|
||||
if (deviceInfo == null) {
|
||||
return@launch
|
||||
}
|
||||
|
||||
val supportedContexts = DeviceManager.getSupportedContexts(deviceInfo)
|
||||
logger.debug("Connected key supports: {}", supportedContexts)
|
||||
if (!supportedContexts.contains(viewModel.appContext.value)) {
|
||||
val preferredContext = DeviceManager.getPreferredContext(supportedContexts)
|
||||
logger.debug(
|
||||
"Current context ({}) is not supported by the key. Using preferred context {}",
|
||||
viewModel.appContext.value,
|
||||
preferredContext
|
||||
)
|
||||
switchContext(preferredContext)
|
||||
}
|
||||
|
||||
if (contextManager == null) {
|
||||
switchContext(DeviceManager.getPreferredContext(supportedContexts))
|
||||
}
|
||||
|
||||
contextManager?.let {
|
||||
try {
|
||||
it.processYubiKey(device)
|
||||
} catch (e: Throwable) {
|
||||
logger.error("Error processing YubiKey in AppContextManager", e)
|
||||
}
|
||||
}
|
||||
processYubiKey(device)
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,7 +350,7 @@ class MainActivity : FlutterFragmentActivity() {
|
||||
|
||||
viewModel.appContext.observe(this) {
|
||||
switchContext(it)
|
||||
viewModel.connectedYubiKey.value?.let(::processYubiKey)
|
||||
viewModel.connectedYubiKey.value?.let(::launchProcessYubiKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,8 @@ VSVersionInfo(
|
||||
ffi=FixedFileInfo(
|
||||
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
|
||||
# Set not needed items to zero 0.
|
||||
filevers=(7, 0, 1, 0),
|
||||
prodvers=(7, 0, 1, 0),
|
||||
filevers=(7, 0, 2, 0),
|
||||
prodvers=(7, 0, 2, 0),
|
||||
# Contains a bitmask that specifies the valid bits 'flags'r
|
||||
mask=0x3f,
|
||||
# Contains a bitmask that specifies the Boolean attributes of the file.
|
||||
@ -31,11 +31,11 @@ VSVersionInfo(
|
||||
'040904b0',
|
||||
[StringStruct('CompanyName', 'Yubico'),
|
||||
StringStruct('FileDescription', 'Yubico Authenticator Helper'),
|
||||
StringStruct('FileVersion', '7.0.1-dev.0'),
|
||||
StringStruct('FileVersion', '7.0.2-dev.0'),
|
||||
StringStruct('LegalCopyright', 'Copyright (c) Yubico'),
|
||||
StringStruct('OriginalFilename', 'authenticator-helper.exe'),
|
||||
StringStruct('ProductName', 'Yubico Authenticator'),
|
||||
StringStruct('ProductVersion', '7.0.1-dev.0')])
|
||||
StringStruct('ProductVersion', '7.0.2-dev.0')])
|
||||
]),
|
||||
VarFileInfo([VarStruct('Translation', [1033, 1200])])
|
||||
]
|
||||
|
@ -17,6 +17,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
@ -177,8 +178,10 @@ class _FidoFingerprintsNotifier extends FidoFingerprintsNotifier {
|
||||
if (json == null) {
|
||||
state = const AsyncValue.loading();
|
||||
} else {
|
||||
List<Fingerprint> newState = List.from(
|
||||
(json as List).map((e) => Fingerprint.fromJson(e)).toList());
|
||||
List<Fingerprint> newState = List.from((json as List)
|
||||
.map((e) => Fingerprint.fromJson(e))
|
||||
.sortedBy<String>((f) => f.label.toLowerCase())
|
||||
.toList());
|
||||
state = AsyncValue.data(newState);
|
||||
}
|
||||
}, onError: (err, stackTrace) {
|
||||
|
@ -207,10 +207,8 @@ final addCredentialsToAnyProvider = Provider(
|
||||
final androidCredentialListProvider = StateNotifierProvider.autoDispose
|
||||
.family<OathCredentialListNotifier, List<OathPair>?, DevicePath>(
|
||||
(ref, devicePath) {
|
||||
var notifier = _AndroidCredentialListNotifier(
|
||||
ref.watch(withContextProvider),
|
||||
ref.watch(currentDeviceProvider)?.transport == Transport.usb,
|
||||
);
|
||||
var notifier =
|
||||
_AndroidCredentialListNotifier(ref.watch(withContextProvider), ref);
|
||||
return notifier;
|
||||
},
|
||||
);
|
||||
@ -218,22 +216,15 @@ final androidCredentialListProvider = StateNotifierProvider.autoDispose
|
||||
class _AndroidCredentialListNotifier extends OathCredentialListNotifier {
|
||||
final _events = const EventChannel('android.oath.credentials');
|
||||
final WithContext _withContext;
|
||||
final bool _isUsbAttached;
|
||||
final Ref _ref;
|
||||
late StreamSubscription _sub;
|
||||
|
||||
_AndroidCredentialListNotifier(this._withContext, this._isUsbAttached)
|
||||
: super() {
|
||||
_AndroidCredentialListNotifier(this._withContext, this._ref) : super() {
|
||||
_sub = _events.receiveBroadcastStream().listen((event) {
|
||||
final json = jsonDecode(event);
|
||||
List<OathPair>? newState = json != null
|
||||
? List.from((json as List).map((e) => OathPair.fromJson(e)).toList())
|
||||
: null;
|
||||
if (state != null && newState == null) {
|
||||
// If we go from non-null to null this means we should stop listening to
|
||||
// avoid receiving a message for a different notifier as there is only
|
||||
// one channel.
|
||||
_sub.cancel();
|
||||
}
|
||||
state = newState;
|
||||
});
|
||||
}
|
||||
@ -249,7 +240,7 @@ class _AndroidCredentialListNotifier extends OathCredentialListNotifier {
|
||||
// Prompt for touch if needed
|
||||
UserInteractionController? controller;
|
||||
Timer? touchTimer;
|
||||
if (_isUsbAttached) {
|
||||
if (_ref.read(currentDeviceProvider)?.transport == Transport.usb) {
|
||||
void triggerTouchPrompt() async {
|
||||
controller = await _withContext(
|
||||
(context) async {
|
||||
|
@ -54,7 +54,7 @@ class _HomeScreenState extends ConsumerState<HomeScreen> {
|
||||
final enabledCapabilities = widget.deviceData.info.config
|
||||
.enabledCapabilities[widget.deviceData.node.transport] ??
|
||||
0;
|
||||
final primaryColor = ref.watch(defaultColorProvider);
|
||||
final primaryColor = ref.watch(primaryColorProvider);
|
||||
|
||||
// We need this to avoid unwanted app switch animation
|
||||
if (hide) {
|
||||
@ -219,19 +219,14 @@ class _DeviceColor extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final theme = Theme.of(context);
|
||||
final primaryColor = ref.watch(defaultColorProvider);
|
||||
final defaultColor =
|
||||
(isAndroid && ref.read(androidSdkVersionProvider) >= 31)
|
||||
? theme.colorScheme.onSurface
|
||||
: primaryColor;
|
||||
final defaultColor = ref.watch(defaultColorProvider);
|
||||
final customColor = initialCustomization.color;
|
||||
|
||||
return ChoiceFilterChip<Color?>(
|
||||
disableHover: true,
|
||||
value: customColor,
|
||||
items: const [null],
|
||||
selected: customColor != null && customColor != defaultColor,
|
||||
selected: customColor != null && customColor.value != defaultColor.value,
|
||||
itemBuilder: (e) => Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
runSpacing: 8,
|
||||
@ -250,32 +245,31 @@ class _DeviceColor extends ConsumerWidget {
|
||||
Colors.lightGreen
|
||||
].map((e) => _ColorButton(
|
||||
color: e,
|
||||
isSelected: customColor == e,
|
||||
isSelected: customColor?.value == e.value,
|
||||
onPressed: () {
|
||||
_updateColor(e, ref);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
)),
|
||||
|
||||
// remove color button
|
||||
// "use default color" button
|
||||
RawMaterialButton(
|
||||
onPressed: () {
|
||||
_updateColor(null, ref);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
constraints: const BoxConstraints(minWidth: 26.0, minHeight: 26.0),
|
||||
fillColor: (isAndroid && ref.read(androidSdkVersionProvider) >= 31)
|
||||
? theme.colorScheme.onSurface
|
||||
: primaryColor,
|
||||
fillColor: defaultColor,
|
||||
hoverColor: Colors.black12,
|
||||
shape: const CircleBorder(),
|
||||
child: Icon(
|
||||
Symbols.cancel,
|
||||
size: 16,
|
||||
color: customColor == null
|
||||
? theme.colorScheme.onSurface
|
||||
: theme.colorScheme.surface.withOpacity(0.2),
|
||||
),
|
||||
child: Icon(customColor == null ? Symbols.circle : Symbols.clear,
|
||||
fill: 1,
|
||||
size: 16,
|
||||
weight: 700,
|
||||
opticalSize: 20,
|
||||
color: defaultColor.computeLuminance() > 0.7
|
||||
? Colors.grey // for bright colors
|
||||
: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -1,5 +1,5 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// This file is generated by running ./set-version.py <version> <build>
|
||||
|
||||
const String version = '7.0.1-dev.0';
|
||||
const int build = 70001;
|
||||
const String version = '7.0.2-dev.0';
|
||||
const int build = 70002;
|
||||
|
@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
# This field is updated by running ./set-version.py <version>
|
||||
# DO NOT MANUALLY EDIT THIS!
|
||||
version: 7.0.1-dev.0+70001
|
||||
version: 7.0.2-dev.0+70002
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
@ -70,7 +70,7 @@ dependencies:
|
||||
io: ^1.0.4
|
||||
base32: ^2.1.3
|
||||
convert: ^3.1.1
|
||||
material_symbols_icons: ^4.2741.0
|
||||
material_symbols_icons: ^4.2719.3
|
||||
|
||||
dev_dependencies:
|
||||
integration_test:
|
||||
|
@ -1,4 +1,4 @@
|
||||
$version="7.0.1-dev.0"
|
||||
$version="7.0.2-dev.0"
|
||||
|
||||
echo "Clean-up of old files"
|
||||
rm *.msi
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
|
||||
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
||||
<?define ProductVersion="7.0.1" ?>
|
||||
<?define ProductVersion="7.0.2" ?>
|
||||
<?define ProductName="Yubico Authenticator" ?>
|
||||
|
||||
<Product Id="*" UpgradeCode="fcbafc57-aaaa-47b8-b861-20bda48cd4f6" Name="$(var.ProductName)" Version="$(var.ProductVersion)" Manufacturer="Yubico AB" Language="1033">
|
||||
|
Loading…
Reference in New Issue
Block a user