diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 19d50c39..989b2b1e 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -35,10 +35,20 @@
+
+
+
+
+
+
+
+
+
+
(NfcAdapter.EXTRA_TAG)
if (tag != null) {
@@ -215,6 +223,7 @@ class MainActivity : FlutterFragmentActivity() {
private lateinit var flutterLog: FlutterLog
private lateinit var flutterStreams: List
private lateinit var appMethodChannel: AppMethodChannel
+ private lateinit var appLinkMethodChannel: AppLinkMethodChannel
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
@@ -226,6 +235,7 @@ class MainActivity : FlutterFragmentActivity() {
dialogManager = DialogManager(messenger, this.lifecycleScope)
appPreferences = AppPreferences(this)
appMethodChannel = AppMethodChannel(messenger)
+ appLinkMethodChannel = AppLinkMethodChannel(messenger)
flutterStreams = listOf(
viewModel.deviceInfo.streamTo(this, messenger, "android.devices.deviceInfo"),
diff --git a/android/app/src/main/kotlin/com/yubico/authenticator/oath/AppLinkMethodChannel.kt b/android/app/src/main/kotlin/com/yubico/authenticator/oath/AppLinkMethodChannel.kt
new file mode 100644
index 00000000..b87399f9
--- /dev/null
+++ b/android/app/src/main/kotlin/com/yubico/authenticator/oath/AppLinkMethodChannel.kt
@@ -0,0 +1,25 @@
+package com.yubico.authenticator.oath
+
+import android.net.Uri
+import androidx.annotation.UiThread
+import com.yubico.authenticator.logging.Log
+import io.flutter.plugin.common.BinaryMessenger
+import io.flutter.plugin.common.MethodChannel
+import org.json.JSONObject
+
+class AppLinkMethodChannel(messenger: BinaryMessenger) {
+ private val methodChannel = MethodChannel(messenger, "app.link.methods")
+
+ @UiThread
+ fun handleUri(uri: Uri) {
+ Log.t(TAG, "Handling URI: $uri")
+ methodChannel.invokeMethod(
+ "handleOtpAuthLink",
+ JSONObject(mapOf("link" to uri.toString())).toString()
+ )
+ }
+
+ companion object {
+ const val TAG = "AppLinkMethodChannel"
+ }
+}
\ No newline at end of file
diff --git a/lib/android/init.dart b/lib/android/init.dart
index de087f3c..2764db3e 100644
--- a/lib/android/init.dart
+++ b/lib/android/init.dart
@@ -8,6 +8,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logging/logging.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:yubico_authenticator/android/logger.dart';
+import 'package:yubico_authenticator/android/oath/otp_auth_link_handler.dart';
import 'package:yubico_authenticator/android/window_state_provider.dart';
import 'package:yubico_authenticator/app/logging.dart';
@@ -54,7 +55,8 @@ Future initialize() async {
windowStateProvider.overrideWithProvider(androidWindowStateProvider),
clipboardProvider.overrideWithProvider(androidClipboardProvider),
androidSdkVersionProvider.overrideWithValue(await getAndroidSdkVersion()),
- supportedThemesProvider.overrideWithProvider(androidSupportedThemesProvider)
+ supportedThemesProvider
+ .overrideWithProvider(androidSupportedThemesProvider)
],
child: DismissKeyboard(
child: YubicoAuthenticatorApp(page: Consumer(
@@ -68,6 +70,9 @@ Future initialize() async {
/// initializes global handler for dialogs
ref.read(androidDialogProvider);
+ /// set context which will handle otpauth links
+ setupOtpAuthLinkHandler(context);
+
return const MainPage();
},
)),
diff --git a/lib/android/oath/otp_auth_link_handler.dart b/lib/android/oath/otp_auth_link_handler.dart
new file mode 100644
index 00000000..61235146
--- /dev/null
+++ b/lib/android/oath/otp_auth_link_handler.dart
@@ -0,0 +1,42 @@
+import 'dart:convert';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+import '../../app/message.dart';
+import '../../oath/models.dart';
+import '../../oath/views/add_account_page.dart';
+
+const _appLinkMethodsChannel = MethodChannel('app.link.methods');
+
+void setupOtpAuthLinkHandler(BuildContext context) {
+ _appLinkMethodsChannel.setMethodCallHandler((call) async {
+ final args = jsonDecode(call.arguments);
+ switch (call.method) {
+ case 'handleOtpAuthLink':
+ {
+ var url = args['link'];
+ var otpauth = CredentialData.fromUri(Uri.parse(url));
+ Navigator.popUntil(context, ModalRoute.withName('/'));
+ await showBlurDialog(
+ context: context,
+ routeSettings: const RouteSettings(name: 'oath_add_account'),
+ builder: (_) {
+ return OathAddAccountPage(
+ null,
+ null,
+ credentials: null,
+ credentialData: otpauth,
+ );
+ },
+ );
+ break;
+ }
+ default:
+ throw PlatformException(
+ code: 'NotImplemented',
+ message: 'Method ${call.method} is not implemented',
+ );
+ }
+ });
+}