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', + ); + } + }); +}