This commit is contained in:
Adam Velebil 2024-05-31 16:28:49 +02:00
commit b0010da643
No known key found for this signature in database
GPG Key ID: C9B1E4A3CBBD2E10
10 changed files with 85 additions and 83 deletions

6
NEWS
View File

@ -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.

View File

@ -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)
}
}

View File

@ -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])])
]

View File

@ -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) {

View File

@ -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 {

View File

@ -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),
),
],
),

View File

@ -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;

View File

@ -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:

View File

@ -1,4 +1,4 @@
$version="7.0.1-dev.0"
$version="7.0.2-dev.0"
echo "Clean-up of old files"
rm *.msi

View File

@ -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">