Remember last used app

This also disables Home functionality on Android.
This commit is contained in:
Elias Bonnici 2024-03-07 12:43:06 +01:00
parent 908d2352dd
commit 6f3137752b
No known key found for this signature in database
GPG Key ID: 5EAC28EA3F980CCF
6 changed files with 37 additions and 40 deletions

View File

@ -23,7 +23,7 @@ import com.yubico.authenticator.device.Info
import com.yubico.yubikit.android.transport.usb.UsbYubiKeyDevice import com.yubico.yubikit.android.transport.usb.UsbYubiKeyDevice
enum class OperationContext(val value: Int) { enum class OperationContext(val value: Int) {
Oath(0), Yubikey(1), Invalid(-1); Home(0), Oath(1), Yubikey(2), Invalid(-1);
companion object { companion object {
fun getByValue(value: Int) = values().firstOrNull { it.value == value } ?: Invalid fun getByValue(value: Int) = values().firstOrNull { it.value == value } ?: Invalid

View File

@ -64,8 +64,8 @@ Future<Widget> initialize() async {
oathStateProvider.overrideWithProvider(androidOathStateProvider.call), oathStateProvider.overrideWithProvider(androidOathStateProvider.call),
credentialListProvider credentialListProvider
.overrideWithProvider(androidCredentialListProvider.call), .overrideWithProvider(androidCredentialListProvider.call),
currentAppProvider.overrideWith( currentAppProvider.overrideWith((ref) => AndroidSubPageNotifier(
(ref) => AndroidSubPageNotifier(ref.watch(supportedAppsProvider))), ref.watch(supportedAppsProvider), ref.watch(prefProvider))),
managementStateProvider.overrideWithProvider(androidManagementState.call), managementStateProvider.overrideWithProvider(androidManagementState.call),
currentDeviceProvider.overrideWith( currentDeviceProvider.overrideWith(
() => AndroidCurrentDeviceNotifier(), () => AndroidCurrentDeviceNotifier(),
@ -95,6 +95,7 @@ Future<Widget> initialize() async {
..setFeature(features.fido, false) ..setFeature(features.fido, false)
..setFeature(features.piv, false) ..setFeature(features.piv, false)
..setFeature(features.otp, false) ..setFeature(features.otp, false)
..setFeature(features.home, false)
..setFeature(features.management, false); ..setFeature(features.management, false);
}); });

View File

@ -91,7 +91,7 @@ final androidSupportedThemesProvider = StateProvider<List<ThemeMode>>((ref) {
}); });
class AndroidSubPageNotifier extends CurrentAppNotifier { class AndroidSubPageNotifier extends CurrentAppNotifier {
AndroidSubPageNotifier(super.supportedApps) { AndroidSubPageNotifier(super.supportedApps, super.prefs) {
_handleSubPage(state); _handleSubPage(state);
} }

View File

@ -203,7 +203,8 @@ abstract class CurrentDeviceNotifier extends Notifier<DeviceNode?> {
final currentAppProvider = final currentAppProvider =
StateNotifierProvider<CurrentAppNotifier, Application>((ref) { StateNotifierProvider<CurrentAppNotifier, Application>((ref) {
final notifier = CurrentAppNotifier(ref.watch(supportedAppsProvider)); final notifier = CurrentAppNotifier(
ref.watch(supportedAppsProvider), ref.watch(prefProvider));
ref.listen<AsyncValue<YubiKeyData>>(currentDeviceDataProvider, (_, data) { ref.listen<AsyncValue<YubiKeyData>>(currentDeviceDataProvider, (_, data) {
notifier._notifyDeviceChanged(data.whenOrNull(data: ((data) => data))); notifier._notifyDeviceChanged(data.whenOrNull(data: ((data) => data)));
}, fireImmediately: true); }, fireImmediately: true);
@ -212,16 +213,29 @@ final currentAppProvider =
class CurrentAppNotifier extends StateNotifier<Application> { class CurrentAppNotifier extends StateNotifier<Application> {
final List<Application> _supportedApps; final List<Application> _supportedApps;
static const String _key = 'APP_STATE_LAST_APP';
final SharedPreferences _prefs;
CurrentAppNotifier(this._supportedApps) : super(_supportedApps.first); CurrentAppNotifier(this._supportedApps, this._prefs)
: super(_fromName(_prefs.getString(_key), _supportedApps));
void setCurrentApp(Application app) { void setCurrentApp(Application app) {
state = app; state = app;
_prefs.setString(_key, app.name);
} }
void _notifyDeviceChanged(YubiKeyData? data) { void _notifyDeviceChanged(YubiKeyData? data) {
if (data == null || if (data == null) {
state.getAvailability(data) != Availability.unsupported) { state = _supportedApps.first;
return;
}
String? lastAppName = _prefs.getString(_key);
if (lastAppName != null && lastAppName != state.name) {
// Try switching to saved app
state = Application.values.firstWhere((app) => app.name == lastAppName);
}
if (state.getAvailability(data) != Availability.unsupported) {
// Keep current app // Keep current app
return; return;
} }
@ -231,6 +245,10 @@ class CurrentAppNotifier extends StateNotifier<Application> {
orElse: () => _supportedApps.first, orElse: () => _supportedApps.first,
); );
} }
static Application _fromName(String? name, List<Application> supportedApps) =>
supportedApps.firstWhere((element) => element.name == name,
orElse: () => supportedApps.first);
} }
abstract class QrScanner { abstract class QrScanner {

View File

@ -19,6 +19,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import '../../core/state.dart';
import '../models.dart'; import '../models.dart';
import '../state.dart'; import '../state.dart';
import 'device_picker.dart'; import 'device_picker.dart';
@ -126,7 +127,9 @@ class NavigationContent extends ConsumerWidget {
.where( .where(
(app) => app.getAvailability(data) != Availability.unsupported) (app) => app.getAvailability(data) != Availability.unsupported)
.toList() .toList()
: [Application.home]; : !isAndroid // TODO: Remove check when Home is implemented on Android
? [Application.home]
: <Application>[];
availableApps.remove(Application.management); availableApps.remove(Application.management);
final currentApp = ref.watch(currentAppProvider); final currentApp = ref.watch(currentAppProvider);
@ -138,9 +141,7 @@ class NavigationContent extends ConsumerWidget {
duration: const Duration(milliseconds: 150), duration: const Duration(milliseconds: 150),
child: DevicePickerContent(extended: extended), child: DevicePickerContent(extended: extended),
), ),
const SizedBox(height: 32), const SizedBox(height: 32),
AnimatedSize( AnimatedSize(
duration: const Duration(milliseconds: 150), duration: const Duration(milliseconds: 150),
child: Column( child: Column(
@ -153,7 +154,7 @@ class NavigationContent extends ConsumerWidget {
Icon(app._icon, fill: app == currentApp ? 1.0 : 0.0), Icon(app._icon, fill: app == currentApp ? 1.0 : 0.0),
collapsed: !extended, collapsed: !extended,
selected: app == currentApp, selected: app == currentApp,
onTap: currentApp == Application.home || onTap: data == null && currentApp == Application.home ||
data != null && data != null &&
app.getAvailability(data) == app.getAvailability(data) ==
Availability.enabled Availability.enabled
@ -171,32 +172,6 @@ class NavigationContent extends ConsumerWidget {
], ],
), ),
), ),
// // Non-YubiKey pages
// NavigationItem(
// leading: const Icon(Icons.settings_outlined),
// key: settingDrawerIcon,
// title: l10n.s_settings,
// collapsed: !extended,
// onTap: () {
// if (shouldPop) {
// Navigator.of(context).pop();
// }
// Actions.maybeInvoke(context, const SettingsIntent());
// },
// ),
// NavigationItem(
// leading: const Icon(Icons.help_outline),
// key: helpDrawerIcon,
// title: l10n.s_help_and_about,
// collapsed: !extended,
// onTap: () {
// if (shouldPop) {
// Navigator.of(context).pop();
// }
// Actions.maybeInvoke(context, const AboutIntent());
// },
// ),
], ],
), ),
); );

View File

@ -21,6 +21,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../app/views/message_page.dart'; import '../../app/views/message_page.dart';
import '../../core/state.dart';
import '../../management/models.dart'; import '../../management/models.dart';
import 'key_actions.dart'; import 'key_actions.dart';
@ -59,13 +60,15 @@ class HomeMessagePage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final l10n = AppLocalizations.of(context)!; final l10n = AppLocalizations.of(context)!;
// TODO: Remove Android check when Home is implemented on Android
return MessagePage( return MessagePage(
title: l10n.s_home, title: !isAndroid ? l10n.s_home : null,
graphic: graphic, graphic: graphic,
header: header, header: header,
message: message, message: message,
footnote: footnote, footnote: footnote,
keyActionsBuilder: (context) => homeBuildActions(context, null, ref), keyActionsBuilder:
!isAndroid ? (context) => homeBuildActions(context, null, ref) : null,
actionButtonBuilder: actionButtonBuilder, actionButtonBuilder: actionButtonBuilder,
actionsBuilder: actionsBuilder, actionsBuilder: actionsBuilder,
fileDropOverlay: fileDropOverlay, fileDropOverlay: fileDropOverlay,