From 604ac192943a1aeb1dd709be9c46005fa64402e8 Mon Sep 17 00:00:00 2001 From: Adam Velebil Date: Thu, 19 Oct 2023 16:35:45 +0200 Subject: [PATCH] initial impl --- android/app/src/main/AndroidManifest.xml | 12 +++- .../com/yubico/authenticator/MainActivity.kt | 67 ++++++++++++++++--- lib/android/models.dart | 6 +- lib/android/state.dart | 13 ++-- lib/l10n/app_en.arb | 1 + test/android_settings_page_test.dart | 2 +- 6 files changed, 84 insertions(+), 17 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 1939fb7e..8a0af79f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -78,6 +78,16 @@ android:exported="true" android:launchMode="singleTop" android:theme="@style/NdefActivityTheme"> + + + + @@ -89,7 +99,7 @@ - + diff --git a/android/app/src/main/kotlin/com/yubico/authenticator/MainActivity.kt b/android/app/src/main/kotlin/com/yubico/authenticator/MainActivity.kt index 6849160f..8e034773 100644 --- a/android/app/src/main/kotlin/com/yubico/authenticator/MainActivity.kt +++ b/android/app/src/main/kotlin/com/yubico/authenticator/MainActivity.kt @@ -97,16 +97,59 @@ class MainActivity : FlutterFragmentActivity() { * * @param enable if true, alias activity will be enabled */ - private fun enableAliasMainActivityComponent(enable: Boolean) { - val componentName = ComponentName(packageName, "com.yubico.authenticator.AliasMainActivity") + private fun enableActivityAlias(aliasName: String, enabledState: Int) { + val componentName = ComponentName(packageName, "com.yubico.authenticator.$aliasName") applicationContext.packageManager.setComponentEnabledSetting( componentName, - if (enable) - PackageManager.COMPONENT_ENABLED_STATE_ENABLED - else - PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, + enabledState, PackageManager.DONT_KILL_APP ) + logger.trace("Activity alias '$aliasName' is enabled: $enabledState") + } + + /** + * Sets state of AliasMainActivity to enabled. This will enable USB discovery. + */ + private fun enableAppUsbDiscovery() { + enableActivityAlias( + "AliasMainActivity", + PackageManager.COMPONENT_ENABLED_STATE_ENABLED + ) + } + + /** + * Sets state of AliasMainActivity to default. This will deactivate the USB discovery. + * + * The default for AliasMainActivity is disabled. + */ + private fun disableAppUsbDiscovery() { + enableActivityAlias( + "AliasMainActivity", + PackageManager.COMPONENT_ENABLED_STATE_DEFAULT + ) + } + + /** + * Sets state of AliasNdefActivity to defaut. This will activate NFC intent filters. + * + * The default for AliasNdefActivity is enabled. + */ + private fun enableAppNfcDiscovery() { + enableActivityAlias( + "AliasNdefActivity", + PackageManager.COMPONENT_ENABLED_STATE_DEFAULT + ) + } + + /** + * Sets state of AliasNdefActivity to disabled. This will deactivate NFC intent filters. + */ + private fun disableAppNfcDiscovery() { + // enable NFC discovery based on user preferences + enableActivityAlias( + "AliasNdefActivity", + PackageManager.COMPONENT_ENABLED_STATE_DISABLED + ) } override fun onNewIntent(intent: Intent) { @@ -180,16 +223,24 @@ class MainActivity : FlutterFragmentActivity() { stopUsbDiscovery() stopNfcDiscovery() + if (!appPreferences.openAppOnUsb) { - enableAliasMainActivityComponent(false) + disableAppUsbDiscovery() } + + if (appPreferences.openAppOnNfcTap || appPreferences.copyOtpOnNfcTap) { + enableAppNfcDiscovery() + } else { + disableAppNfcDiscovery() + } + super.onPause() } override fun onResume() { super.onResume() - enableAliasMainActivityComponent(true) + enableAppUsbDiscovery() // Handle opening through otpauth:// link val intentData = intent.data diff --git a/lib/android/models.dart b/lib/android/models.dart index 22074a52..4e3f3cdc 100755 --- a/lib/android/models.dart +++ b/lib/android/models.dart @@ -17,13 +17,15 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; enum NfcTapAction { + noAction, launch, copy, - both; + launchAndCopy; String getDescription(AppLocalizations l10n) => switch (this) { + NfcTapAction.noAction => l10n.l_do_nothing, NfcTapAction.launch => l10n.l_launch_ya, NfcTapAction.copy => l10n.l_copy_otp_clipboard, - NfcTapAction.both => l10n.l_launch_and_copy_otp + NfcTapAction.launchAndCopy => l10n.l_launch_and_copy_otp }; } diff --git a/lib/android/state.dart b/lib/android/state.dart index ea1b46b8..f9d0e537 100644 --- a/lib/android/state.dart +++ b/lib/android/state.dart @@ -142,12 +142,13 @@ class NfcTapActionNotifier extends StateNotifier { final copyOtp = prefs.getBool(_prefNfcCopyOtp) ?? false; final NfcTapAction action; if (launchApp && copyOtp) { - action = NfcTapAction.both; + action = NfcTapAction.launchAndCopy; } else if (copyOtp) { action = NfcTapAction.copy; - } else { - // This is the default value if both are false. + } else if (launchApp) { action = NfcTapAction.launch; + } else { + action = NfcTapAction.noAction; } return NfcTapActionNotifier._(prefs, action); } @@ -155,8 +156,10 @@ class NfcTapActionNotifier extends StateNotifier { Future setTapAction(NfcTapAction value) async { if (state != value) { state = value; - await _prefs.setBool(_prefNfcOpenApp, value != NfcTapAction.copy); - await _prefs.setBool(_prefNfcCopyOtp, value != NfcTapAction.launch); + await _prefs.setBool(_prefNfcOpenApp, + value == NfcTapAction.launch || value == NfcTapAction.launchAndCopy); + await _prefs.setBool(_prefNfcCopyOtp, + value == NfcTapAction.copy || value == NfcTapAction.launchAndCopy); } } } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index d041dcff..eccd8923 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -579,6 +579,7 @@ "@_android_settings": {}, "s_nfc_options": "NFC options", "l_on_yk_nfc_tap": "On YubiKey NFC tap", + "l_do_nothing": "Do nothing", "l_launch_ya": "Launch Yubico Authenticator", "l_copy_otp_clipboard": "Copy OTP to clipboard", "l_launch_and_copy_otp": "Launch app and copy OTP", diff --git a/test/android_settings_page_test.dart b/test/android_settings_page_test.dart index 5662de6b..574b94a3 100644 --- a/test/android_settings_page_test.dart +++ b/test/android_settings_page_test.dart @@ -66,7 +66,7 @@ extension _WidgetTesterHelper on WidgetTester { Future selectBothOption() async { await openNfcTapOptionSelection(); - await tap(find.byKey(android_keys.nfcTapOption(NfcTapAction.both))); + await tap(find.byKey(android_keys.nfcTapOption(NfcTapAction.launchAndCopy))); await pumpAndSettle(); }