From e2296d4a9ecb2fb93cc00e3e21a5a0453ed611c0 Mon Sep 17 00:00:00 2001 From: Pavlo Bilohaienko Date: Wed, 3 Nov 2021 13:26:42 +0200 Subject: [PATCH 01/13] KAA-747 Impelement target screen search on screen --- ios/Classes/SwiftFlutterWebAuthPlugin.swift | 31 ++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/ios/Classes/SwiftFlutterWebAuthPlugin.swift b/ios/Classes/SwiftFlutterWebAuthPlugin.swift index e4fe4748..732c918c 100644 --- a/ios/Classes/SwiftFlutterWebAuthPlugin.swift +++ b/ios/Classes/SwiftFlutterWebAuthPlugin.swift @@ -46,7 +46,7 @@ public class SwiftFlutterWebAuthPlugin: NSObject, FlutterPlugin { let session = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackURLScheme, completionHandler: completionHandler) if #available(iOS 13, *) { - guard let provider = UIApplication.shared.delegate?.window??.rootViewController as? FlutterViewController else { + guard let provider = UIApplication.shared.visibleViewController(FlutterViewController.self) else { result(FlutterError(code: "FAILED", message: "Failed to aquire root FlutterViewController" , details: nil)) return } @@ -79,3 +79,32 @@ extension FlutterViewController: ASWebAuthenticationPresentationContextProviding return self.view.window! } } + +extension UIApplication { + + /// Getting the VC in hierarchy by type. + /// Supporting navigation, tab bar and presented controllers. + /// Not tested with different view hierarchies and iOS versions yet. + func visibleViewController(_ target: Target.Type) -> Target? { + let root = UIApplication.shared.windows.first?.rootViewController + var visibleController: UIViewController? + if let targetRoot = root as? Target { + // Root is the target controller + visibleController = targetRoot + } else if let navigationRoot = root as? UINavigationController { + // Getting modal VC if exists. Otherwise the top view controller. + visibleController = navigationRoot.topViewController + } else if let tabRoot = root as? UITabBarController { + // Getting modal VC if exists. + // Otherwise the selectedViewController of tab bar controller. + visibleController = tabRoot.selectedViewController + } + if let modalController = visibleController?.presentedViewController as? Target { + return modalController + } else { + // If target not found as modal and the type cast below fails, + // the target controller is not presented in hierarchy. + return visibleController as? Target + } + } +} From 8819e652310fe5657eca3de88db9be609994ebed Mon Sep 17 00:00:00 2001 From: Yegor Logachev Date: Wed, 3 Nov 2021 16:57:45 +0200 Subject: [PATCH 02/13] Returning result for android has been re-worked, cleanUpDanglingCalls has been removed from plugin, process of receiving of callback uri has been refactored, CallbackActivity has been removed as redundant --- android/build.gradle | 12 +- .../flutter_web_auth/CallbackActivity.kt | 20 -- .../flutter_web_auth/FlutterWebAuthPlugin.kt | 190 +++++++++++++----- example/android/app/build.gradle | 6 +- .../android/app/src/main/AndroidManifest.xml | 13 -- .../flutter_web_auth_example/MainActivity.kt | 12 +- example/android/build.gradle | 4 +- example/android/gradle.properties | 1 - example/pubspec.lock | 8 +- ios/Classes/SwiftFlutterWebAuthPlugin.swift | 3 - lib/flutter_web_auth.dart | 27 --- pubspec.lock | 8 +- 12 files changed, 164 insertions(+), 140 deletions(-) delete mode 100644 android/src/main/kotlin/com/linusu/flutter_web_auth/CallbackActivity.kt diff --git a/android/build.gradle b/android/build.gradle index 4fda61f8..ffc3802d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,14 +2,14 @@ group 'com.linusu.flutter_web_auth' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.3.72' + ext.kotlin_version = '1.5.21' repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.3' + classpath 'com.android.tools.build:gradle:3.6.4' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -17,7 +17,7 @@ buildscript { rootProject.allprojects { repositories { google() - jcenter() + mavenCentral() } } @@ -25,7 +25,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { - compileSdkVersion 28 + compileSdkVersion 29 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -39,7 +39,7 @@ android { } dependencies { - implementation 'androidx.browser:browser:1.0.0' + implementation 'androidx.browser:browser:1.3.0' } } diff --git a/android/src/main/kotlin/com/linusu/flutter_web_auth/CallbackActivity.kt b/android/src/main/kotlin/com/linusu/flutter_web_auth/CallbackActivity.kt deleted file mode 100644 index 5e523085..00000000 --- a/android/src/main/kotlin/com/linusu/flutter_web_auth/CallbackActivity.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.linusu.flutter_web_auth - -import android.app.Activity -import android.net.Uri -import android.os.Bundle - -class CallbackActivity: Activity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val url = intent?.data - val scheme = url?.scheme - - if (scheme != null) { - FlutterWebAuthPlugin.callbacks.remove(scheme)?.success(url.toString()) - } - - finish() - } -} diff --git a/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt b/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt index 520dcddc..bfd8a2f6 100644 --- a/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt +++ b/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt @@ -1,74 +1,170 @@ package com.linusu.flutter_web_auth -import android.content.Context +import android.app.Activity +import android.app.Application import android.content.Intent import android.net.Uri - +import android.os.Bundle +import android.util.Log import androidx.browser.customtabs.CustomTabsIntent - +import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.embedding.engine.plugins.activity.ActivityAware +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result -import io.flutter.plugin.common.PluginRegistry.Registrar +import io.flutter.plugin.common.PluginRegistry + +class FlutterWebAuthPlugin( + private var channel: MethodChannel? = null, + private var activityBinding: ActivityPluginBinding? = null +) : MethodCallHandler, FlutterPlugin, ActivityAware, PluginRegistry.NewIntentListener { + + private lateinit var lifecycleListener: ActivityLifecycleListener + + private val activity: Activity + get() = activityBinding!!.activity -class FlutterWebAuthPlugin(private var context: Context? = null, private var channel: MethodChannel? = null): MethodCallHandler, FlutterPlugin { - companion object { - val callbacks = mutableMapOf() + companion object { + val callbacks = mutableMapOf() + } + + private fun initInstance(messenger: BinaryMessenger) { + channel = MethodChannel(messenger, "flutter_web_auth") + channel?.setMethodCallHandler(this) + } + + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + initInstance(binding.binaryMessenger) + } - @JvmStatic - fun registerWith(registrar: Registrar) { - val plugin = FlutterWebAuthPlugin() - plugin.initInstance(registrar.messenger(), registrar.context()) + override fun onAttachedToActivity(binding: ActivityPluginBinding) { + activityBinding = binding.apply { + addOnNewIntentListener(this@FlutterWebAuthPlugin) + lifecycleListener = ActivityLifecycleListener(activity.javaClass.name) { + cleanUpDanglingCalls() + } + activity.application.registerActivityLifecycleCallbacks(lifecycleListener) + } } - } + override fun onDetachedFromActivityForConfigChanges() { + activityBinding?.apply { + removeOnNewIntentListener(this@FlutterWebAuthPlugin) + activity.application.unregisterActivityLifecycleCallbacks(lifecycleListener) + } + activityBinding = null + } - fun initInstance(messenger: BinaryMessenger, context: Context) { - this.context = context - channel = MethodChannel(messenger, "flutter_web_auth") - channel?.setMethodCallHandler(this) - } + override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { + activityBinding = binding.apply { + addOnNewIntentListener(this@FlutterWebAuthPlugin) + activity.application.registerActivityLifecycleCallbacks(lifecycleListener) + } + } - override public fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { - initInstance(binding.getBinaryMessenger(), binding.getApplicationContext()) - } + override fun onDetachedFromActivity() { + activityBinding?.apply { + removeOnNewIntentListener(this@FlutterWebAuthPlugin) + activity.application.unregisterActivityLifecycleCallbacks(lifecycleListener) + } + activityBinding = null + } - override public fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - context = null - channel = null - } - override fun onMethodCall(call: MethodCall, resultCallback: Result) { - when (call.method) { - "authenticate" -> { - val url = Uri.parse(call.argument("url")) - val callbackUrlScheme = call.argument("callbackUrlScheme")!! - val preferEphemeral = call.argument("preferEphemeral")!! + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + channel = null + } - callbacks[callbackUrlScheme] = resultCallback + override fun onMethodCall(call: MethodCall, resultCallback: Result) { + when (call.method) { + "authenticate" -> { + val url = Uri.parse(call.argument("url")) + val callbackUrlScheme = call.argument("callbackUrlScheme")!! + val preferEphemeral = call.argument("preferEphemeral")!! - val intent = CustomTabsIntent.Builder().build() - val keepAliveIntent = Intent(context, KeepAliveService::class.java) + callbacks[callbackUrlScheme] = resultCallback - intent.intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) - if (preferEphemeral) { - intent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) - } - intent.intent.putExtra("android.support.customtabs.extra.KEEP_ALIVE", keepAliveIntent) + val intent = CustomTabsIntent.Builder().build() + val keepAliveIntent = Intent(activity, KeepAliveService::class.java) - intent.launchUrl(context, url) + intent.intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) + if (preferEphemeral) { + intent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) + } + intent.intent.putExtra( + "android.support.customtabs.extra.KEEP_ALIVE", + keepAliveIntent + ) + intent.intent.data = url + intent.launchUrl(activity, url) + } + else -> resultCallback.notImplemented() } - "cleanUpDanglingCalls" -> { - callbacks.forEach{ (_, danglingResultCallback) -> - danglingResultCallback.error("CANCELED", "User canceled login", null) - } - callbacks.clear() - resultCallback.success(null) + } + + private fun cleanUpDanglingCalls() { + if (callbacks.isNotEmpty()) { + callbacks.forEach { (_, danglingResultCallback) -> + danglingResultCallback.error("CANCELED", "User canceled login", null) + } + callbacks.clear() } - else -> resultCallback.notImplemented() } - } + + override fun onNewIntent(intent: Intent?): Boolean { + if (intent != null && intent.action == Intent.ACTION_VIEW && + intent.hasCategory(Intent.CATEGORY_BROWSABLE) + ) { + val url = intent.data + val scheme = url?.scheme + callbacks.remove(scheme)?.success(url?.toString()) + activity.finish() + return true + } + return false + } } + +private class ActivityLifecycleListener( + val appActivityName: String, + val onReturnFromBrowser: () -> Unit +) : Application.ActivityLifecycleCallbacks { + + var paused: Boolean = false + + override fun onActivityPaused(activity: Activity) { + verifyActivity(activity) { + paused = true + } + } + + override fun onActivityResumed(activity: Activity) { + verifyActivity(activity) { + if (paused) { + Log.d("FlutterWebAuthPlugin", "onReturnFromBrowser") + onReturnFromBrowser() + paused = false + } + } + } + + fun verifyActivity(activity: Activity, success: () -> Unit) { + if (activity.javaClass.name == appActivityName && activity is FlutterActivity) { + success() + } + } + + override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {} + + override fun onActivityStarted(activity: Activity) {} + + override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {} + + override fun onActivityStopped(activity: Activity?) {} + + override fun onActivityDestroyed(activity: Activity?) {} +} \ No newline at end of file diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 1182eace..c9c3bb6b 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 28 + compileSdkVersion 29 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -40,7 +40,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.linusu.flutter_web_auth_example" minSdkVersion 16 - targetSdkVersion 28 + targetSdkVersion 29 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -61,7 +61,7 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' } diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 113eb6cc..47d8339c 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -7,7 +7,6 @@ additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> - - - - - - - - - - diff --git a/example/android/app/src/main/kotlin/com/linusu/flutter_web_auth_example/MainActivity.kt b/example/android/app/src/main/kotlin/com/linusu/flutter_web_auth_example/MainActivity.kt index b1be57f2..c7440bbd 100644 --- a/example/android/app/src/main/kotlin/com/linusu/flutter_web_auth_example/MainActivity.kt +++ b/example/android/app/src/main/kotlin/com/linusu/flutter_web_auth_example/MainActivity.kt @@ -1,13 +1,5 @@ package com.linusu.flutter_web_auth_example -import android.os.Bundle +import io.flutter.embedding.android.FlutterActivity -import io.flutter.app.FlutterActivity -import io.flutter.plugins.GeneratedPluginRegistrant - -class MainActivity: FlutterActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - GeneratedPluginRegistrant.registerWith(this) - } -} +class MainActivity: FlutterActivity() diff --git a/example/android/build.gradle b/example/android/build.gradle index 45d37ba1..457e6980 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.3.72' + ext.kotlin_version = '1.5.21' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.3' + classpath 'com.android.tools.build:gradle:3.6.4' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index a6738207..94adc3a3 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,4 +1,3 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -android.enableR8=true diff --git a/example/pubspec.lock b/example/pubspec.lock index 573b7d80..d4b70a6b 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.6.1" + version: "2.8.1" boolean_selector: dependency: transitive description: @@ -28,7 +28,7 @@ packages: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" clock: dependency: transitive description: @@ -87,7 +87,7 @@ packages: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" path: dependency: transitive description: @@ -141,7 +141,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.4.2" typed_data: dependency: transitive description: diff --git a/ios/Classes/SwiftFlutterWebAuthPlugin.swift b/ios/Classes/SwiftFlutterWebAuthPlugin.swift index e4fe4748..ca1f2733 100644 --- a/ios/Classes/SwiftFlutterWebAuthPlugin.swift +++ b/ios/Classes/SwiftFlutterWebAuthPlugin.swift @@ -64,9 +64,6 @@ public class SwiftFlutterWebAuthPlugin: NSObject, FlutterPlugin { } else { result(FlutterError(code: "FAILED", message: "This plugin does currently not support iOS lower than iOS 11" , details: nil)) } - } else if (call.method == "cleanUpDanglingCalls") { - // we do not keep track of old callbacks on iOS, so nothing to do here - result(nil) } else { result(FlutterMethodNotImplemented) } diff --git a/lib/flutter_web_auth.dart b/lib/flutter_web_auth.dart index a57099eb..8a7ee70d 100644 --- a/lib/flutter_web_auth.dart +++ b/lib/flutter_web_auth.dart @@ -1,27 +1,10 @@ import 'dart:async'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart' show MethodChannel; -class _OnAppLifecycleResumeObserver extends WidgetsBindingObserver { - final Function onResumed; - - _OnAppLifecycleResumeObserver(this.onResumed); - - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - if (state == AppLifecycleState.resumed) { - onResumed(); - } - } -} - class FlutterWebAuth { static const MethodChannel _channel = const MethodChannel('flutter_web_auth'); - static final _OnAppLifecycleResumeObserver _resumedObserver = _OnAppLifecycleResumeObserver(() { - _cleanUpDanglingCalls(); // unawaited - }); /// Ask the user to authenticate to the specified web service. /// @@ -30,20 +13,10 @@ class FlutterWebAuth { /// [callbackUrlScheme] should be a string specifying the scheme of the url that the page will redirect to upon successful authentication. /// [preferEphemeral] if this is specified as `true`, an ephemeral web browser session will be used where possible (`FLAG_ACTIVITY_NO_HISTORY` on Android, `prefersEphemeralWebBrowserSession` on iOS/macOS) static Future authenticate({required String url, required String callbackUrlScheme, bool? preferEphemeral}) async { - WidgetsBinding.instance?.removeObserver(_resumedObserver); // safety measure so we never add this observer twice - WidgetsBinding.instance?.addObserver(_resumedObserver); return await _channel.invokeMethod('authenticate', { 'url': url, 'callbackUrlScheme': callbackUrlScheme, 'preferEphemeral': preferEphemeral ?? false, }) as String; } - - /// On Android, the plugin has to store the Result callbacks in order to pass the result back to the caller of - /// `authenticate`. But if that result never comes the callback will dangle around forever. This can be called to - /// terminate all `authenticate` calls with an error. - static Future _cleanUpDanglingCalls() async { - await _channel.invokeMethod('cleanUpDanglingCalls'); - WidgetsBinding.instance?.removeObserver(_resumedObserver); - } } diff --git a/pubspec.lock b/pubspec.lock index 9e82bb73..d4896e9e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.6.1" + version: "2.8.1" boolean_selector: dependency: transitive description: @@ -28,7 +28,7 @@ packages: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" clock: dependency: transitive description: @@ -73,7 +73,7 @@ packages: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" path: dependency: transitive description: @@ -127,7 +127,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.4.2" typed_data: dependency: transitive description: From d8d3861cf29822b81ce65bf9c0b892c3cb1fee65 Mon Sep 17 00:00:00 2001 From: Yegor Logachev Date: Wed, 3 Nov 2021 19:22:47 +0200 Subject: [PATCH 03/13] fixed issue with closing activity after receiving callback url --- .../kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt b/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt index bfd8a2f6..c9b6ead3 100644 --- a/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt +++ b/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt @@ -122,7 +122,6 @@ class FlutterWebAuthPlugin( val url = intent.data val scheme = url?.scheme callbacks.remove(scheme)?.success(url?.toString()) - activity.finish() return true } return false From b233de6dcb018181caaf8b1f35bd582e740e6c07 Mon Sep 17 00:00:00 2001 From: Pavlo Bilohaienko Date: Wed, 3 Nov 2021 21:14:18 +0200 Subject: [PATCH 04/13] KAA-747 Fixing target controller search on screen --- ios/Classes/SwiftFlutterWebAuthPlugin.swift | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/ios/Classes/SwiftFlutterWebAuthPlugin.swift b/ios/Classes/SwiftFlutterWebAuthPlugin.swift index 7d5e8f76..bc5402f1 100644 --- a/ios/Classes/SwiftFlutterWebAuthPlugin.swift +++ b/ios/Classes/SwiftFlutterWebAuthPlugin.swift @@ -81,20 +81,16 @@ extension UIApplication { /// Getting the VC in hierarchy by type. /// Supporting navigation, tab bar and presented controllers. - /// Not tested with different view hierarchies and iOS versions yet. func visibleViewController(_ target: Target.Type) -> Target? { let root = UIApplication.shared.windows.first?.rootViewController var visibleController: UIViewController? - if let targetRoot = root as? Target { - // Root is the target controller - visibleController = targetRoot - } else if let navigationRoot = root as? UINavigationController { - // Getting modal VC if exists. Otherwise the top view controller. + if let navigationRoot = root as? UINavigationController { visibleController = navigationRoot.topViewController } else if let tabRoot = root as? UITabBarController { - // Getting modal VC if exists. - // Otherwise the selectedViewController of tab bar controller. visibleController = tabRoot.selectedViewController + } else { + // Common UIViewController + visibleController = root } if let modalController = visibleController?.presentedViewController as? Target { return modalController From 102ec04e9bd25551c3a8a3364c9da95207a3bd3c Mon Sep 17 00:00:00 2001 From: Pavlo Bilohaienko Date: Thu, 4 Nov 2021 11:34:47 +0200 Subject: [PATCH 05/13] KAA-747 Extending delegate with plugin's protocol --- ios/Classes/SwiftFlutterWebAuthPlugin.swift | 33 ++++----------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/ios/Classes/SwiftFlutterWebAuthPlugin.swift b/ios/Classes/SwiftFlutterWebAuthPlugin.swift index bc5402f1..cf7fe9d1 100644 --- a/ios/Classes/SwiftFlutterWebAuthPlugin.swift +++ b/ios/Classes/SwiftFlutterWebAuthPlugin.swift @@ -3,6 +3,10 @@ import SafariServices import Flutter import UIKit +public protocol FlutterPresentationContextProviding: UIApplicationDelegate { + var flutterController: FlutterViewController { get } +} + public class SwiftFlutterWebAuthPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "flutter_web_auth", binaryMessenger: registrar.messenger()) @@ -46,11 +50,11 @@ public class SwiftFlutterWebAuthPlugin: NSObject, FlutterPlugin { let session = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackURLScheme, completionHandler: completionHandler) if #available(iOS 13, *) { - guard let provider = UIApplication.shared.visibleViewController(FlutterViewController.self) else { + guard let appDelegate = UIApplication.shared.delegate as? FlutterPresentationContextProviding else { result(FlutterError(code: "FAILED", message: "Failed to aquire root FlutterViewController" , details: nil)) return } - + let provider = appDelegate.flutterController session.prefersEphemeralWebBrowserSession = preferEphemeral session.presentationContextProvider = provider } @@ -76,28 +80,3 @@ extension FlutterViewController: ASWebAuthenticationPresentationContextProviding return self.view.window! } } - -extension UIApplication { - - /// Getting the VC in hierarchy by type. - /// Supporting navigation, tab bar and presented controllers. - func visibleViewController(_ target: Target.Type) -> Target? { - let root = UIApplication.shared.windows.first?.rootViewController - var visibleController: UIViewController? - if let navigationRoot = root as? UINavigationController { - visibleController = navigationRoot.topViewController - } else if let tabRoot = root as? UITabBarController { - visibleController = tabRoot.selectedViewController - } else { - // Common UIViewController - visibleController = root - } - if let modalController = visibleController?.presentedViewController as? Target { - return modalController - } else { - // If target not found as modal and the type cast below fails, - // the target controller is not presented in hierarchy. - return visibleController as? Target - } - } -} From 9bf4876946a4b19c35037e267a7fa3320fd93980 Mon Sep 17 00:00:00 2001 From: Pavlo Bilohaienko Date: Thu, 4 Nov 2021 11:39:35 +0200 Subject: [PATCH 06/13] KAA-747 Adding cooments --- ios/Classes/SwiftFlutterWebAuthPlugin.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ios/Classes/SwiftFlutterWebAuthPlugin.swift b/ios/Classes/SwiftFlutterWebAuthPlugin.swift index cf7fe9d1..6c8e66fc 100644 --- a/ios/Classes/SwiftFlutterWebAuthPlugin.swift +++ b/ios/Classes/SwiftFlutterWebAuthPlugin.swift @@ -3,6 +3,8 @@ import SafariServices import Flutter import UIKit +/// Conform your `AppDelegate` to the protocol +/// in order to get `ASWebAuthenticationSession` work public protocol FlutterPresentationContextProviding: UIApplicationDelegate { var flutterController: FlutterViewController { get } } @@ -51,6 +53,8 @@ public class SwiftFlutterWebAuthPlugin: NSObject, FlutterPlugin { if #available(iOS 13, *) { guard let appDelegate = UIApplication.shared.delegate as? FlutterPresentationContextProviding else { + // Will receive nil if AppDelegate doesn't conform + // to the `FlutterPresentationContextProviding` protocol result(FlutterError(code: "FAILED", message: "Failed to aquire root FlutterViewController" , details: nil)) return } From e71f85a437bf2524c26157213b684fba777df334 Mon Sep 17 00:00:00 2001 From: Pavlo Bilohaienko Date: Mon, 8 Nov 2021 16:06:53 +0200 Subject: [PATCH 07/13] KAA-747 Fixing type matching --- ios/Classes/SwiftFlutterWebAuthPlugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Classes/SwiftFlutterWebAuthPlugin.swift b/ios/Classes/SwiftFlutterWebAuthPlugin.swift index 6c8e66fc..3d115766 100644 --- a/ios/Classes/SwiftFlutterWebAuthPlugin.swift +++ b/ios/Classes/SwiftFlutterWebAuthPlugin.swift @@ -6,7 +6,7 @@ import UIKit /// Conform your `AppDelegate` to the protocol /// in order to get `ASWebAuthenticationSession` work public protocol FlutterPresentationContextProviding: UIApplicationDelegate { - var flutterController: FlutterViewController { get } + var flutterController: FlutterViewController? { get } } public class SwiftFlutterWebAuthPlugin: NSObject, FlutterPlugin { From 389967c0d0c725f35388dbc8e8413f69c6c1a315 Mon Sep 17 00:00:00 2001 From: Yegor Logachev Date: Mon, 8 Nov 2021 16:38:04 +0200 Subject: [PATCH 08/13] android build compile version has been changed to 31 --- android/build.gradle | 2 +- .../flutter_web_auth/FlutterWebAuthPlugin.kt | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index ffc3802d..fa266032 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { - compileSdkVersion 29 + compileSdkVersion 31 sourceSets { main.java.srcDirs += 'src/main/kotlin' diff --git a/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt b/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt index c9b6ead3..eb8055df 100644 --- a/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt +++ b/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt @@ -141,6 +141,12 @@ private class ActivityLifecycleListener( } } + override fun onActivityStopped(activity: Activity) {} + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} + + override fun onActivityDestroyed(activity: Activity) {} + override fun onActivityResumed(activity: Activity) { verifyActivity(activity) { if (paused) { @@ -157,13 +163,7 @@ private class ActivityLifecycleListener( } } - override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {} + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} override fun onActivityStarted(activity: Activity) {} - - override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {} - - override fun onActivityStopped(activity: Activity?) {} - - override fun onActivityDestroyed(activity: Activity?) {} } \ No newline at end of file From 10c98ff6d1f2d7416b6ffc91056717b77448c75f Mon Sep 17 00:00:00 2001 From: Yegor Logachev Date: Tue, 7 Dec 2021 17:26:55 +0200 Subject: [PATCH 09/13] Chrome browser package has been added to custom tabs launch intent if Chrome installed --- android/build.gradle | 2 +- .../linusu/flutter_web_auth/FlutterWebAuthPlugin.kt | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index fa266032..4af6079f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -39,7 +39,7 @@ android { } dependencies { - implementation 'androidx.browser:browser:1.3.0' + implementation 'androidx.browser:browser:1.4.0' } } diff --git a/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt b/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt index eb8055df..2ff5e21c 100644 --- a/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt +++ b/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt @@ -3,8 +3,10 @@ package com.linusu.flutter_web_auth import android.app.Activity import android.app.Application import android.content.Intent +import android.content.pm.PackageManager import android.net.Uri import android.os.Bundle +import android.text.TextUtils import android.util.Log import androidx.browser.customtabs.CustomTabsIntent import io.flutter.embedding.android.FlutterActivity @@ -99,6 +101,14 @@ class FlutterWebAuthPlugin( "android.support.customtabs.extra.KEEP_ALIVE", keepAliveIntent ) + val CHROME_PACKAGE_NAME = "com.android.chrome" + val resolveInfoList = activity.packageManager.queryIntentActivities(intent.intent, PackageManager.MATCH_DEFAULT_ONLY) + for (resolveInfo in resolveInfoList) { + val packageName = resolveInfo.activityInfo.packageName + if (TextUtils.equals(packageName, CHROME_PACKAGE_NAME)) + intent.intent.setPackage(CHROME_PACKAGE_NAME) + } + intent.intent.data = url intent.launchUrl(activity, url) } From 3e14acb08129e8c06e4746f96f90ad416288d7ab Mon Sep 17 00:00:00 2001 From: Yegor Logachev Date: Tue, 7 Dec 2021 17:56:27 +0200 Subject: [PATCH 10/13] adroidx browser library version has been downgrated to 1.3.0 --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 4af6079f..fa266032 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -39,7 +39,7 @@ android { } dependencies { - implementation 'androidx.browser:browser:1.4.0' + implementation 'androidx.browser:browser:1.3.0' } } From 24380a22b82dc13db15e757de64aed7a68dd74d7 Mon Sep 17 00:00:00 2001 From: Yegor Logachev Date: Tue, 7 Dec 2021 18:42:09 +0200 Subject: [PATCH 11/13] Showing chrome browser if exist has been fixed and works properly --- android/build.gradle | 2 +- .../kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index fa266032..8616d917 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -31,7 +31,7 @@ android { main.java.srcDirs += 'src/main/kotlin' } defaultConfig { - minSdkVersion 16 + minSdkVersion 23 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } lintOptions { diff --git a/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt b/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt index 2ff5e21c..88692629 100644 --- a/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt +++ b/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt @@ -102,7 +102,8 @@ class FlutterWebAuthPlugin( keepAliveIntent ) val CHROME_PACKAGE_NAME = "com.android.chrome" - val resolveInfoList = activity.packageManager.queryIntentActivities(intent.intent, PackageManager.MATCH_DEFAULT_ONLY) + val resolveInfoList = activity.packageManager.queryIntentActivities(intent + .intent, PackageManager.MATCH_ALL) for (resolveInfo in resolveInfoList) { val packageName = resolveInfo.activityInfo.packageName if (TextUtils.equals(packageName, CHROME_PACKAGE_NAME)) From 4ab8eb719a0fc7c15dcccb22659c7320f377b744 Mon Sep 17 00:00:00 2001 From: Yegor Logachev Date: Tue, 7 Dec 2021 18:48:23 +0200 Subject: [PATCH 12/13] step when we add url to intent has been replaced --- .../kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt b/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt index 88692629..3212ad69 100644 --- a/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt +++ b/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt @@ -101,6 +101,7 @@ class FlutterWebAuthPlugin( "android.support.customtabs.extra.KEEP_ALIVE", keepAliveIntent ) + intent.intent.data = url val CHROME_PACKAGE_NAME = "com.android.chrome" val resolveInfoList = activity.packageManager.queryIntentActivities(intent .intent, PackageManager.MATCH_ALL) @@ -109,8 +110,6 @@ class FlutterWebAuthPlugin( if (TextUtils.equals(packageName, CHROME_PACKAGE_NAME)) intent.intent.setPackage(CHROME_PACKAGE_NAME) } - - intent.intent.data = url intent.launchUrl(activity, url) } else -> resultCallback.notImplemented() From 4fdab8c92421e6db9f7f0d9da3e56dc5c1238578 Mon Sep 17 00:00:00 2001 From: Andrey Komarichev Date: Tue, 31 May 2022 16:35:58 +0300 Subject: [PATCH 13/13] KAA-1037: fixed override of onNewIntent by making Intent non-nullable --- .../com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt b/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt index 3212ad69..45307033 100644 --- a/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt +++ b/android/src/main/kotlin/com/linusu/flutter_web_auth/FlutterWebAuthPlugin.kt @@ -125,8 +125,8 @@ class FlutterWebAuthPlugin( } } - override fun onNewIntent(intent: Intent?): Boolean { - if (intent != null && intent.action == Intent.ACTION_VIEW && + override fun onNewIntent(intent: Intent): Boolean { + if (intent.action == Intent.ACTION_VIEW && intent.hasCategory(Intent.CATEGORY_BROWSABLE) ) { val url = intent.data