Skip to content

Commit

Permalink
Merge PR Yubico#1232.
Browse files Browse the repository at this point in the history
  • Loading branch information
AdamVe committed Oct 20, 2023
2 parents b40c8ea + db736ed commit 75c39d7
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 73 deletions.
12 changes: 11 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/NdefActivityTheme">
</activity>

<activity-alias
android:name=".AliasNdefActivity"
android:exported="true"
android:enabled="true"
android:launchMode="singleTop"
android:theme="@style/NdefActivityTheme"
android:targetActivity=".NdefActivity">

<intent-filter
android:autoVerify="true"
tools:ignore="UnusedAttribute">
Expand All @@ -89,7 +99,7 @@

</intent-filter>

</activity>
</activity-alias>

<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
Expand Down
109 changes: 109 additions & 0 deletions android/app/src/main/kotlin/com/yubico/authenticator/ActivityUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright (C) 2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.yubico.authenticator

import android.app.Activity
import android.content.ComponentName
import android.content.pm.PackageManager
import org.slf4j.LoggerFactory

class ActivityUtil(private val activity: Activity) {

private val logger = LoggerFactory.getLogger(ActivityUtil::class.java)

/**
* The app will be started when a complaint USB device is attached.
*
* Calling this method will enable <code>AliasMainActivity</code> alias which contains
* intent-filter for android.hardware.usb.action.USB_DEVICE_ATTACHED. This alias is disabled by
* default in the AndroidManifest.xml.
*
* Devices which will activate the intent filter are defined in `res/xml/device_filter.xml`.
* @see <a href="https://developer.android.com/guide/topics/manifest/activity-alias-element">Activity Alias in Android SDK documentation</a>
*/
fun enableSystemUsbDiscovery() {
setState(
MAIN_ACTIVITY_ALIAS,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
)
}

/**
* The system will not start this app when a complaint USB device is attached.
*
* Calling this method will disable <code>AliasMainActivity</code> alias and the intent-filter
* for android.hardware.usb.action.USB_DEVICE_ATTACHED will not be active.
*
* @see <a href="https://developer.android.com/guide/topics/manifest/activity-alias-element">Activity Alias in Android SDK documentation</a>
*/
fun disableSystemUsbDiscovery() {
setState(
MAIN_ACTIVITY_ALIAS,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
)
}

/**
* The system will start the app when an NDEF tag with recognized URI is discovered.
*
* Calling this method will enable <code>AliasNdefActivity</code> alias and the intent-filter
* for android.nfc.action.NDEF_DISCOVERED will be active. This is the default behavior as
* defined in the AndroidManifest.xml.
*
* For list of discoverable URIs see the alias definition in AndroidManifest.xml.
*
* @see <a href="https://developer.android.com/guide/topics/manifest/activity-alias-element">Activity Alias in Android SDK documentation</a>
*/
fun enableAppNfcDiscovery() {
setState(
NDEF_ACTIVITY_ALIAS,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
)
}

/**
* The system will ignore the app when NDEF tags are discovered.
*
* Calling this method will disable <code>AliasNdefActivity</code> alias and there will be no
* active intent-filter for android.nfc.action.NDEF_DISCOVERED.
*
* @see <a href="https://developer.android.com/guide/topics/manifest/activity-alias-element">Activity Alias in Android SDK documentation</a>
*/
fun disableAppNfcDiscovery() {
setState(
NDEF_ACTIVITY_ALIAS,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED
)
}

private fun setState(aliasName: String, enabledState: Int) {
val componentName =
ComponentName(activity.packageName, "com.yubico.authenticator.$aliasName")
activity.applicationContext.packageManager.setComponentEnabledSetting(
componentName,
enabledState,
PackageManager.DONT_KILL_APP
)
logger.trace("Activity alias '$aliasName' is enabled: $enabledState")
}

companion object {
const val NDEF_ACTIVITY_ALIAS = "AliasNdefActivity"
const val MAIN_ACTIVITY_ALIAS = "AliasMainActivity"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class MainActivity : FlutterFragmentActivity() {
// receives broadcasts when QR Scanner camera is closed
private val qrScannerCameraClosedBR = QRScannerCameraClosedBR()
private val nfcAdapterStateChangeBR = NfcAdapterStateChangedBR()
private val activityUtil = ActivityUtil(this)

private val logger = LoggerFactory.getLogger(MainActivity::class.java)

Expand All @@ -88,27 +89,6 @@ class MainActivity : FlutterFragmentActivity() {
yubikit = YubiKitManager(this)
}

/**
* Enables or disables .AliasMainActivity component. This activity alias adds intent-filter
* for android.hardware.usb.action.USB_DEVICE_ATTACHED. When enabled, the app will be opened
* when a compliant USB device (defined in `res/xml/device_filter.xml`) is attached.
*
* By default the activity alias is disabled through AndroidManifest.xml.
*
* @param enable if true, alias activity will be enabled
*/
private fun enableAliasMainActivityComponent(enable: Boolean) {
val componentName = ComponentName(packageName, "com.yubico.authenticator.AliasMainActivity")
applicationContext.packageManager.setComponentEnabledSetting(
componentName,
if (enable)
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
else
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
PackageManager.DONT_KILL_APP
)
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
Expand Down Expand Up @@ -180,16 +160,24 @@ class MainActivity : FlutterFragmentActivity() {

stopUsbDiscovery()
stopNfcDiscovery()

if (!appPreferences.openAppOnUsb) {
enableAliasMainActivityComponent(false)
activityUtil.disableSystemUsbDiscovery()
}

if (appPreferences.openAppOnNfcTap || appPreferences.copyOtpOnNfcTap) {
activityUtil.enableAppNfcDiscovery()
} else {
activityUtil.disableAppNfcDiscovery()
}

super.onPause()
}

override fun onResume() {
super.onResume()

enableAliasMainActivityComponent(true)
activityUtil.enableSystemUsbDiscovery()

// Handle opening through otpauth:// link
val intentData = intent.data
Expand Down
6 changes: 4 additions & 2 deletions lib/android/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
}
13 changes: 8 additions & 5 deletions lib/android/state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -142,21 +142,24 @@ class NfcTapActionNotifier extends StateNotifier<NfcTapAction> {
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);
}

Future<void> 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);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion lib/android/views/settings_views.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ class NfcKbdLayoutView extends ConsumerWidget {
title: Text(l10n.l_kbd_layout_for_static),
subtitle: Text(clipKbdLayout),
key: keys.nfcKeyboardLayoutSetting,
enabled: tapAction != NfcTapAction.launch,
enabled: tapAction == NfcTapAction.copy ||
tapAction == NfcTapAction.launchAndCopy,
onTap: () async {
final newValue = await _selectKbdLayout(
context,
Expand Down
6 changes: 5 additions & 1 deletion lib/app/views/settings_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../../android/state.dart';
import '../../android/views/settings_views.dart';
import '../../core/state.dart';
import '../../widgets/list_title.dart';
Expand Down Expand Up @@ -122,12 +123,15 @@ class SettingsPage extends ConsumerWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (isAndroid) ...[
// add nfc options only on devices with NFC capability
if (isAndroid && ref.watch(androidNfcSupportProvider)) ...[
ListTitle(l10n.s_nfc_options),
const NfcTapActionView(),
const NfcKbdLayoutView(),
const NfcBypassTouchView(),
const NfcSilenceSoundsView(),
],
if (isAndroid) ...[
ListTitle(l10n.s_usb_options),
const UsbOpenAppView(),
],
Expand Down
1 change: 1 addition & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading

0 comments on commit 75c39d7

Please sign in to comment.