@@ -2,8 +2,10 @@ package com.linusu.flutter_web_auth_2
22
33import android.content.Context
44import android.content.Intent
5+ import android.content.pm.PackageManager
56import android.net.Uri
6-
7+ import android.os.Build
8+ import androidx.browser.customtabs.CustomTabsClient
79import androidx.browser.customtabs.CustomTabsIntent
810
911import io.flutter.embedding.engine.plugins.FlutterPlugin
@@ -13,51 +15,136 @@ import io.flutter.plugin.common.MethodChannel
1315import io.flutter.plugin.common.MethodChannel.MethodCallHandler
1416import io.flutter.plugin.common.MethodChannel.Result
1517
16- class FlutterWebAuth2Plugin (private var context : Context ? = null , private var channel : MethodChannel ? = null ): MethodCallHandler, FlutterPlugin {
17- companion object {
18- val callbacks = mutableMapOf<String , Result >()
19- }
18+ class FlutterWebAuth2Plugin (
19+ private var context : Context ? = null ,
20+ private var channel : MethodChannel ? = null
21+ ) : MethodCallHandler, FlutterPlugin {
22+ companion object {
23+ val callbacks = mutableMapOf<String , Result >()
24+ }
25+
26+ private fun initInstance (messenger : BinaryMessenger , context : Context ) {
27+ this .context = context
28+ channel = MethodChannel (messenger, " flutter_web_auth_2" )
29+ channel?.setMethodCallHandler(this )
30+ }
2031
21- fun initInstance (messenger : BinaryMessenger , context : Context ) {
22- this .context = context
23- channel = MethodChannel (messenger, " flutter_web_auth_2" )
24- channel?.setMethodCallHandler(this )
25- }
32+ override fun onAttachedToEngine (binding : FlutterPlugin .FlutterPluginBinding ) {
33+ initInstance(binding.binaryMessenger, binding.applicationContext)
34+ }
2635
27- override fun onAttachedToEngine (binding : FlutterPlugin .FlutterPluginBinding ) {
28- initInstance(binding.binaryMessenger, binding.applicationContext)
29- }
36+ override fun onDetachedFromEngine (binding : FlutterPlugin .FlutterPluginBinding ) {
37+ context = null
38+ channel = null
39+ }
3040
31- override fun onDetachedFromEngine (binding : FlutterPlugin .FlutterPluginBinding ) {
32- context = null
33- channel = null
34- }
41+ override fun onMethodCall (call : MethodCall , resultCallback : Result ) {
42+ when (call.method) {
43+ " authenticate" -> {
44+ val url = Uri .parse(call.argument(" url" ))
45+ val callbackUrlScheme = call.argument<String >(" callbackUrlScheme" )!!
46+ val options = call.argument<Map <String , Any >>(" options" )!!
3547
36- override fun onMethodCall (call : MethodCall , resultCallback : Result ) {
37- when (call.method) {
38- " authenticate" -> {
39- val url = Uri .parse(call.argument(" url" ))
40- val callbackUrlScheme = call.argument<String >(" callbackUrlScheme" )!!
41- val options = call.argument<Map <String , Any >>(" options" )!!
48+ callbacks[callbackUrlScheme] = resultCallback
49+ val intent = CustomTabsIntent .Builder ().build()
50+ val keepAliveIntent = Intent (context, KeepAliveService ::class .java)
4251
43- callbacks[callbackUrlScheme] = resultCallback
52+ intent.intent.addFlags(options[" intentFlags" ] as Int )
53+ intent.intent.putExtra(" android.support.customtabs.extra.KEEP_ALIVE" , keepAliveIntent)
54+
55+ val targetPackage = findTargetBrowserPackageName(options)
56+ if (targetPackage != null ) {
57+ intent.intent.setPackage(targetPackage)
58+ }
59+ intent.launchUrl(context!! , url)
60+ }
61+
62+ " cleanUpDanglingCalls" -> {
63+ callbacks.forEach { (_, danglingResultCallback) ->
64+ danglingResultCallback.error(" CANCELED" , " User canceled login" , null )
65+ }
66+ callbacks.clear()
67+ resultCallback.success(null )
68+ }
69+
70+ else -> resultCallback.notImplemented()
71+ }
72+ }
4473
45- val intent = CustomTabsIntent .Builder ().build()
46- val keepAliveIntent = Intent (context, KeepAliveService ::class .java)
74+ /* *
75+ * Find Support CustomTabs Browser.
76+ *
77+ * Priority:
78+ * 1. Chrome
79+ * 2. Custom Browser Order
80+ * 3. default Browser
81+ * 4. Installed Browser
82+ */
83+ private fun findTargetBrowserPackageName (options : Map <String , Any >): String? {
84+ val chromePackage = " com.android.chrome"
85+ // if installed chrome, use chrome at first
86+ if (isSupportCustomTabs(chromePackage)) {
87+ return chromePackage
88+ }
4789
48- intent.intent.addFlags(options[" intentFlags" ] as Int )
49- intent.intent.putExtra(" android.support.customtabs.extra.KEEP_ALIVE" , keepAliveIntent)
90+ @Suppress(" UNCHECKED_CAST" )
91+ val customTabsPackageOrder = (options[" customTabsPackageOrder" ] as Iterable <String >? ) ? : emptyList()
92+ // check target browser
93+ var targetPackage = customTabsPackageOrder.firstOrNull { isSupportCustomTabs(it) }
94+ if (targetPackage != null ) {
95+ return targetPackage
96+ }
5097
51- intent.launchUrl(context!! , url)
98+ // check default browser
99+ val defaultBrowserSupported = CustomTabsClient .getPackageName(context!! , emptyList<String >()) != null
100+ if (defaultBrowserSupported) {
101+ return null ;
52102 }
53- " cleanUpDanglingCalls" -> {
54- callbacks.forEach{ (_, danglingResultCallback) ->
55- danglingResultCallback.error(" CANCELED" , " User canceled login" , null )
56- }
57- callbacks.clear()
58- resultCallback.success(null )
103+ // check installed browser
104+ val allBrowsers = getInstalledBrowsers()
105+ targetPackage = allBrowsers.firstOrNull { isSupportCustomTabs(it) }
106+
107+ return targetPackage
108+ }
109+
110+ private fun getInstalledBrowsers (): List <String > {
111+ // Get all apps that can handle VIEW intents
112+ val activityIntent = Intent (Intent .ACTION_VIEW , Uri .parse(" http://" ))
113+ val packageManager = context!! .packageManager
114+ val viewIntentHandlers = if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) {
115+ packageManager.queryIntentActivities(activityIntent, PackageManager .MATCH_ALL )
116+ } else {
117+ packageManager.queryIntentActivities(activityIntent, 0 )
59118 }
60- else -> resultCallback.notImplemented()
119+
120+ val allBrowser = viewIntentHandlers.map { it.activityInfo.packageName }.sortedWith(compareBy {
121+ if (setOf (
122+ " com.android.chrome" ,
123+ " com.chrome.beta" ,
124+ " com.chrome.dev" ,
125+ " com.microsoft.emmx"
126+ ).contains(it)
127+ ) {
128+ return @compareBy - 1
129+ }
130+
131+ // FireFox default is not enable ,must enable in the browser settings.
132+ if (setOf (" org.mozilla.firefox" ).contains(it)) {
133+ return @compareBy 1
134+ }
135+ return @compareBy 0
136+ })
137+
138+ return allBrowser
61139 }
62- }
140+
141+ private fun isSupportCustomTabs (packageName : String ): Boolean {
142+ val value = CustomTabsClient .getPackageName(
143+ context!! ,
144+ arrayListOf (packageName),
145+ true
146+ )
147+ return value == packageName
148+ }
149+
63150}
0 commit comments