Skip to content

Commit af583aa

Browse files
committed
Keep full authentication proedure on the same task stack for better ux on android
1 parent 8057fa8 commit af583aa

5 files changed

Lines changed: 119 additions & 20 deletions

File tree

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.linusu.flutter_web_auth_2
2+
3+
import android.app.Activity
4+
import android.content.Context
5+
import android.content.Intent
6+
import android.net.Uri
7+
import android.os.Bundle
8+
import androidx.browser.customtabs.CustomTabsIntent
9+
10+
class AuthenticationManagementActivity(
11+
) : Activity() {
12+
companion object {
13+
const val KEY_AUTH_STARTED: String = "authStarted"
14+
const val KEY_AUTH_URI: String = "authUri"
15+
const val KEY_AUTH_OPTION_INTENT_FLAGS: String = "authOptionsIntentFlags"
16+
const val KEY_AUTH_OPTION_TARGET_PACKAGE: String = "authOptionsTargetPackage"
17+
18+
fun createResponseHandlingIntent(context: Context): Intent {
19+
val intent = Intent(context, AuthenticationManagementActivity::class.java)
20+
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
21+
return intent
22+
}
23+
}
24+
private var authStarted: Boolean = false
25+
private lateinit var authenticationUri: Uri
26+
private var intentFlags: Int = 0
27+
private var targetPackage: String? = null
28+
29+
override fun onCreate(savedInstanceState: Bundle?) {
30+
super.onCreate(savedInstanceState)
31+
if (savedInstanceState == null) {
32+
extractState(intent.extras)
33+
} else {
34+
extractState(savedInstanceState)
35+
}
36+
}
37+
38+
override fun onResume() {
39+
super.onResume()
40+
41+
if (!authStarted) {
42+
val intent = CustomTabsIntent.Builder().build()
43+
intent.intent.addFlags(intentFlags)
44+
45+
if (targetPackage != null) {
46+
intent.intent.setPackage(targetPackage)
47+
}
48+
intent.launchUrl(this, authenticationUri)
49+
authStarted = true
50+
return
51+
}
52+
/* If the authentication was already started and we've returned here, the user either
53+
* completed or cancelled authentication.
54+
* Either way we want to return to our original flutter activity, so just finish here
55+
*/
56+
finish()
57+
}
58+
59+
override fun onNewIntent(intent: Intent?) {
60+
super.onNewIntent(intent)
61+
setIntent(intent);
62+
}
63+
64+
override fun onSaveInstanceState(outState: Bundle) {
65+
super.onSaveInstanceState(outState)
66+
outState.putBoolean(KEY_AUTH_STARTED, authStarted)
67+
outState.putParcelable(KEY_AUTH_URI, authenticationUri)
68+
outState.putInt(KEY_AUTH_OPTION_INTENT_FLAGS, intentFlags)
69+
outState.putString(
70+
KEY_AUTH_OPTION_TARGET_PACKAGE,
71+
targetPackage
72+
)
73+
}
74+
75+
private fun extractState(state: Bundle?) {
76+
if (state == null) {
77+
finish()
78+
return
79+
}
80+
authStarted = state.getBoolean(KEY_AUTH_STARTED, false)
81+
authenticationUri = state.getParcelable(KEY_AUTH_URI)!!
82+
intentFlags = state.getInt(KEY_AUTH_OPTION_INTENT_FLAGS, 0)
83+
targetPackage = state.getString(KEY_AUTH_OPTION_TARGET_PACKAGE)
84+
}
85+
}

flutter_web_auth_2/android/src/main/kotlin/com/linusu/flutter_web_auth_2/CallbackActivity.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ class CallbackActivity : Activity() {
1818
if (scheme != null) {
1919
FlutterWebAuth2Plugin.callbacks.remove(scheme)?.success(url.toString())
2020
}
21-
finishAndRemoveTask()
21+
startActivity(AuthenticationManagementActivity.createResponseHandlingIntent(this))
22+
finish()
2223
}
2324

2425

flutter_web_auth_2/android/src/main/kotlin/com/linusu/flutter_web_auth_2/FlutterWebAuth2Plugin.kt

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.linusu.flutter_web_auth_2
22

3+
import android.app.Activity
34
import android.content.Context
45
import android.content.Intent
56
import android.content.pm.PackageManager
@@ -9,16 +10,20 @@ import androidx.browser.customtabs.CustomTabsClient
910
import androidx.browser.customtabs.CustomTabsIntent
1011

1112
import io.flutter.embedding.engine.plugins.FlutterPlugin
13+
import io.flutter.embedding.engine.plugins.activity.ActivityAware
14+
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
1215
import io.flutter.plugin.common.BinaryMessenger
1316
import io.flutter.plugin.common.MethodCall
1417
import io.flutter.plugin.common.MethodChannel
1518
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
1619
import io.flutter.plugin.common.MethodChannel.Result
20+
import java.util.ArrayList
1721

1822
class FlutterWebAuth2Plugin(
1923
private var context: Context? = null,
20-
private var channel: MethodChannel? = null
21-
) : MethodCallHandler, FlutterPlugin {
24+
private var channel: MethodChannel? = null,
25+
private var activity: Activity? = null,
26+
) : MethodCallHandler, FlutterPlugin, ActivityAware {
2227
companion object {
2328
val callbacks = mutableMapOf<String, Result>()
2429
}
@@ -46,17 +51,11 @@ class FlutterWebAuth2Plugin(
4651
val options = call.argument<Map<String, Any>>("options")!!
4752

4853
callbacks[callbackUrlScheme] = resultCallback
49-
val intent = CustomTabsIntent.Builder().build()
50-
val keepAliveIntent = Intent(context, KeepAliveService::class.java)
51-
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)
54+
activity?.startActivity(Intent(activity,AuthenticationManagementActivity::class.java).apply {
55+
putExtra(AuthenticationManagementActivity.KEY_AUTH_URI,url)
56+
putExtra(AuthenticationManagementActivity.KEY_AUTH_OPTION_INTENT_FLAGS, options["intentFlags"] as Int)
57+
putExtra(AuthenticationManagementActivity.KEY_AUTH_OPTION_TARGET_PACKAGE, findTargetBrowserPackageName(options))
58+
})
6059
}
6160

6261
"cleanUpDanglingCalls" -> {
@@ -71,6 +70,22 @@ class FlutterWebAuth2Plugin(
7170
}
7271
}
7372

73+
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
74+
activity = binding.activity
75+
}
76+
77+
override fun onDetachedFromActivityForConfigChanges() {
78+
onDetachedFromActivity()
79+
}
80+
81+
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
82+
onAttachedToActivity(binding)
83+
}
84+
85+
override fun onDetachedFromActivity() {
86+
activity = null
87+
}
88+
7489
/**
7590
* Find Support CustomTabs Browser.
7691
*
@@ -145,5 +160,4 @@ class FlutterWebAuth2Plugin(
145160
)
146161
return value == packageName
147162
}
148-
149163
}

flutter_web_auth_2/example/android/app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
<data android:mimeType="text/plain" />
5050
</intent-filter>
5151
</activity>
52+
<activity android:name="com.linusu.flutter_web_auth_2.AuthenticationManagementActivity">
53+
</activity>
5254
<!-- Don't delete the meta-data below.
5355
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
5456
<meta-data

flutter_web_auth_2/lib/src/options.dart

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
/// Default intent flags for opening the custom tabs intent on Android.
2-
/// This is essentially the same as
3-
/// `FLAG_ACTIVITY_SINGLE_TOP | FLAG_ACTIVITY_NEW_TASK`.
4-
const defaultIntentFlags = 1 << 29 | 1 << 28;
2+
const defaultIntentFlags = 0;
53

64
/// "Ephemeral" intent flags for opening the custom tabs intent on Android.
75
/// This is essentially the same as
8-
/// `FLAG_ACTIVITY_SINGLE_TOP | FLAG_ACTIVITY_NEW_TASK
9-
/// | FLAG_ACTIVITY_NO_HISTORY`.
6+
/// FLAG_ACTIVITY_NO_HISTORY`.
107
const ephemeralIntentFlags = defaultIntentFlags | 1 << 30;
118

129
/// Default HTML code that generates a nice callback page.

0 commit comments

Comments
 (0)