Skip to content

Commit 492d045

Browse files
committed
Merge PR #1575
2 parents e23f599 + 5e9dfbc commit 492d045

File tree

4 files changed

+65
-69
lines changed

4 files changed

+65
-69
lines changed

android/app/src/main/kotlin/com/yubico/authenticator/MainActivity.kt

+41-33
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,13 @@ class MainActivity : FlutterFragmentActivity() {
108108
logger.debug("Starting nfc discovery")
109109
yubikit.startNfcDiscovery(
110110
nfcConfiguration.disableNfcDiscoverySound(appPreferences.silenceNfcSounds),
111-
this,
112-
::processYubiKey
113-
)
111+
this
112+
) { nfcYubiKeyDevice ->
113+
if (!deviceManager.isUsbKeyConnected()) {
114+
launchProcessYubiKey(nfcYubiKeyDevice)
115+
}
116+
}
117+
114118
hasNfc = true
115119
} catch (e: NfcNotAvailable) {
116120
hasNfc = false
@@ -131,7 +135,7 @@ class MainActivity : FlutterFragmentActivity() {
131135
logger.debug("YubiKey was disconnected, stopping usb discovery")
132136
stopUsbDiscovery()
133137
}
134-
processYubiKey(device)
138+
launchProcessYubiKey(device)
135139
}
136140
}
137141

@@ -214,7 +218,7 @@ class MainActivity : FlutterFragmentActivity() {
214218
val device = NfcYubiKeyDevice(tag, nfcConfiguration.timeout, executor)
215219
lifecycleScope.launch {
216220
try {
217-
contextManager?.processYubiKey(device)
221+
processYubiKey(device)
218222
device.remove {
219223
executor.shutdown()
220224
startNfcDiscovery()
@@ -269,41 +273,45 @@ class MainActivity : FlutterFragmentActivity() {
269273
}
270274
}
271275

272-
private fun processYubiKey(device: YubiKeyDevice) {
273-
lifecycleScope.launch {
274-
val deviceInfo = getDeviceInfo(device)
275-
deviceManager.setDeviceInfo(deviceInfo)
276+
private suspend fun processYubiKey(device: YubiKeyDevice) {
277+
val deviceInfo = getDeviceInfo(device)
278+
deviceManager.setDeviceInfo(deviceInfo)
276279

277-
if (deviceInfo == null) {
278-
return@launch
279-
}
280+
if (deviceInfo == null) {
281+
return
282+
}
280283

281-
val supportedContexts = DeviceManager.getSupportedContexts(deviceInfo)
282-
logger.debug("Connected key supports: {}", supportedContexts)
283-
if (!supportedContexts.contains(viewModel.appContext.value)) {
284-
val preferredContext = DeviceManager.getPreferredContext(supportedContexts)
285-
logger.debug(
286-
"Current context ({}) is not supported by the key. Using preferred context {}",
287-
viewModel.appContext.value,
288-
preferredContext
289-
)
290-
switchContext(preferredContext)
291-
}
284+
val supportedContexts = DeviceManager.getSupportedContexts(deviceInfo)
285+
logger.debug("Connected key supports: {}", supportedContexts)
286+
if (!supportedContexts.contains(viewModel.appContext.value)) {
287+
val preferredContext = DeviceManager.getPreferredContext(supportedContexts)
288+
logger.debug(
289+
"Current context ({}) is not supported by the key. Using preferred context {}",
290+
viewModel.appContext.value,
291+
preferredContext
292+
)
293+
switchContext(preferredContext)
294+
}
292295

293-
if (contextManager == null) {
294-
switchContext(DeviceManager.getPreferredContext(supportedContexts))
295-
}
296+
if (contextManager == null) {
297+
switchContext(DeviceManager.getPreferredContext(supportedContexts))
298+
}
296299

297-
contextManager?.let {
298-
try {
299-
it.processYubiKey(device)
300-
} catch (e: Throwable) {
301-
logger.error("Error processing YubiKey in AppContextManager", e)
302-
}
300+
contextManager?.let {
301+
try {
302+
it.processYubiKey(device)
303+
} catch (e: Throwable) {
304+
logger.error("Error processing YubiKey in AppContextManager", e)
303305
}
304306
}
305307
}
306308

309+
private fun launchProcessYubiKey(device: YubiKeyDevice) {
310+
lifecycleScope.launch {
311+
processYubiKey(device)
312+
}
313+
}
314+
307315
private var contextManager: AppContextManager? = null
308316
private lateinit var deviceManager: DeviceManager
309317
private lateinit var appContext: AppContext
@@ -342,7 +350,7 @@ class MainActivity : FlutterFragmentActivity() {
342350

343351
viewModel.appContext.observe(this) {
344352
switchContext(it)
345-
viewModel.connectedYubiKey.value?.let(::processYubiKey)
353+
viewModel.connectedYubiKey.value?.let(::launchProcessYubiKey)
346354
}
347355
}
348356

lib/android/fido/state.dart

+5-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import 'dart:async';
1818
import 'dart:convert';
1919

20+
import 'package:collection/collection.dart';
2021
import 'package:flutter/services.dart';
2122
import 'package:flutter_riverpod/flutter_riverpod.dart';
2223
import 'package:logging/logging.dart';
@@ -177,8 +178,10 @@ class _FidoFingerprintsNotifier extends FidoFingerprintsNotifier {
177178
if (json == null) {
178179
state = const AsyncValue.loading();
179180
} else {
180-
List<Fingerprint> newState = List.from(
181-
(json as List).map((e) => Fingerprint.fromJson(e)).toList());
181+
List<Fingerprint> newState = List.from((json as List)
182+
.map((e) => Fingerprint.fromJson(e))
183+
.sortedBy<String>((f) => f.label.toLowerCase())
184+
.toList());
182185
state = AsyncValue.data(newState);
183186
}
184187
}, onError: (err, stackTrace) {

lib/android/oath/state.dart

+5-14
Original file line numberDiff line numberDiff line change
@@ -207,33 +207,24 @@ final addCredentialsToAnyProvider = Provider(
207207
final androidCredentialListProvider = StateNotifierProvider.autoDispose
208208
.family<OathCredentialListNotifier, List<OathPair>?, DevicePath>(
209209
(ref, devicePath) {
210-
var notifier = _AndroidCredentialListNotifier(
211-
ref.watch(withContextProvider),
212-
ref.watch(currentDeviceProvider)?.transport == Transport.usb,
213-
);
210+
var notifier =
211+
_AndroidCredentialListNotifier(ref.watch(withContextProvider), ref);
214212
return notifier;
215213
},
216214
);
217215

218216
class _AndroidCredentialListNotifier extends OathCredentialListNotifier {
219217
final _events = const EventChannel('android.oath.credentials');
220218
final WithContext _withContext;
221-
final bool _isUsbAttached;
219+
final Ref _ref;
222220
late StreamSubscription _sub;
223221

224-
_AndroidCredentialListNotifier(this._withContext, this._isUsbAttached)
225-
: super() {
222+
_AndroidCredentialListNotifier(this._withContext, this._ref) : super() {
226223
_sub = _events.receiveBroadcastStream().listen((event) {
227224
final json = jsonDecode(event);
228225
List<OathPair>? newState = json != null
229226
? List.from((json as List).map((e) => OathPair.fromJson(e)).toList())
230227
: null;
231-
if (state != null && newState == null) {
232-
// If we go from non-null to null this means we should stop listening to
233-
// avoid receiving a message for a different notifier as there is only
234-
// one channel.
235-
_sub.cancel();
236-
}
237228
state = newState;
238229
});
239230
}
@@ -249,7 +240,7 @@ class _AndroidCredentialListNotifier extends OathCredentialListNotifier {
249240
// Prompt for touch if needed
250241
UserInteractionController? controller;
251242
Timer? touchTimer;
252-
if (_isUsbAttached) {
243+
if (_ref.read(currentDeviceProvider)?.transport == Transport.usb) {
253244
void triggerTouchPrompt() async {
254245
controller = await _withContext(
255246
(context) async {

lib/home/views/home_screen.dart

+14-20
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class _HomeScreenState extends ConsumerState<HomeScreen> {
5454
final enabledCapabilities = widget.deviceData.info.config
5555
.enabledCapabilities[widget.deviceData.node.transport] ??
5656
0;
57-
final primaryColor = ref.watch(defaultColorProvider);
57+
final primaryColor = ref.watch(primaryColorProvider);
5858

5959
// We need this to avoid unwanted app switch animation
6060
if (hide) {
@@ -219,19 +219,14 @@ class _DeviceColor extends ConsumerWidget {
219219
@override
220220
Widget build(BuildContext context, WidgetRef ref) {
221221
final l10n = AppLocalizations.of(context)!;
222-
final theme = Theme.of(context);
223-
final primaryColor = ref.watch(defaultColorProvider);
224-
final defaultColor =
225-
(isAndroid && ref.read(androidSdkVersionProvider) >= 31)
226-
? theme.colorScheme.onSurface
227-
: primaryColor;
222+
final defaultColor = ref.watch(defaultColorProvider);
228223
final customColor = initialCustomization.color;
229224

230225
return ChoiceFilterChip<Color?>(
231226
disableHover: true,
232227
value: customColor,
233228
items: const [null],
234-
selected: customColor != null && customColor != defaultColor,
229+
selected: customColor != null && customColor.value != defaultColor.value,
235230
itemBuilder: (e) => Wrap(
236231
alignment: WrapAlignment.center,
237232
runSpacing: 8,
@@ -250,32 +245,31 @@ class _DeviceColor extends ConsumerWidget {
250245
Colors.lightGreen
251246
].map((e) => _ColorButton(
252247
color: e,
253-
isSelected: customColor == e,
248+
isSelected: customColor?.value == e.value,
254249
onPressed: () {
255250
_updateColor(e, ref);
256251
Navigator.of(context).pop();
257252
},
258253
)),
259254

260-
// remove color button
255+
// "use default color" button
261256
RawMaterialButton(
262257
onPressed: () {
263258
_updateColor(null, ref);
264259
Navigator.of(context).pop();
265260
},
266261
constraints: const BoxConstraints(minWidth: 26.0, minHeight: 26.0),
267-
fillColor: (isAndroid && ref.read(androidSdkVersionProvider) >= 31)
268-
? theme.colorScheme.onSurface
269-
: primaryColor,
262+
fillColor: defaultColor,
270263
hoverColor: Colors.black12,
271264
shape: const CircleBorder(),
272-
child: Icon(
273-
Symbols.cancel,
274-
size: 16,
275-
color: customColor == null
276-
? theme.colorScheme.onSurface
277-
: theme.colorScheme.surface.withOpacity(0.2),
278-
),
265+
child: Icon(customColor == null ? Symbols.circle : Symbols.clear,
266+
fill: 1,
267+
size: 16,
268+
weight: 700,
269+
opticalSize: 20,
270+
color: defaultColor.computeLuminance() > 0.7
271+
? Colors.grey // for bright colors
272+
: Colors.white),
279273
),
280274
],
281275
),

0 commit comments

Comments
 (0)