@@ -20,12 +20,6 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin
20
20
import io.flutter.embedding.engine.plugins.activity.ActivityAware
21
21
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
22
22
import io.flutter.plugin.common.PluginRegistry
23
- import kotlinx.coroutines.CoroutineName
24
- import kotlinx.coroutines.CoroutineScope
25
- import kotlinx.coroutines.Dispatchers
26
- import kotlinx.coroutines.delay
27
- import kotlinx.coroutines.launch
28
- import kotlinx.coroutines.plus
29
23
import java.util.Locale
30
24
31
25
open class AmplifyAuthCognitoPlugin :
@@ -43,9 +37,9 @@ open class AmplifyAuthCognitoPlugin :
43
37
const val CUSTOM_TAB_REQUEST_CODE = 8888
44
38
45
39
/* *
46
- * The scope in which to spawn tasks which should not be awaited from the main thread .
40
+ * An intent extra used to signal the cancellation of a Hosted UI sign-in/sign-out .
47
41
*/
48
- val scope = CoroutineScope ( Dispatchers . IO ) + CoroutineName ( " amplify_flutter.AuthCognito " )
42
+ const val CUSTOM_TAB_CANCEL_EXTRA = " com.amazonaws.amplify.auth.hosted_ui.cancel "
49
43
}
50
44
51
45
/* *
@@ -194,6 +188,7 @@ open class AmplifyAuthCognitoPlugin :
194
188
if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
195
189
packageManager.getPackageInfo(packageName, PackageInfoFlags .of(0 )).versionName
196
190
} else {
191
+ @Suppress(" DEPRECATION" )
197
192
packageManager.getPackageInfo(packageName, 0 ).versionName
198
193
}
199
194
} catch (e: PackageManager .NameNotFoundException ) {
@@ -310,6 +305,8 @@ open class AmplifyAuthCognitoPlugin :
310
305
/* *
311
306
* The application ID of the installed package which can handle custom tabs,
312
307
* typically a browser like Chrome.
308
+ *
309
+ * Adapted from: https://github.com/GoogleChrome/custom-tabs-client/blob/f55501961a211a92eacbe3c2f15d7c58c19c8ef9/shared/src/main/java/org/chromium/customtabsclient/shared/CustomTabsHelper.java#L64
313
310
*/
314
311
private val browserPackageName: String? by lazy {
315
312
val packageManager = mainActivity!! .packageManager
@@ -319,10 +316,18 @@ open class AmplifyAuthCognitoPlugin :
319
316
addCategory(Intent .CATEGORY_BROWSABLE )
320
317
data = Uri .fromParts(" https" , " " , null )
321
318
}
322
- val defaultViewHandlerInfo = packageManager.resolveActivity(
323
- activityIntent,
324
- MATCH_DEFAULT_ONLY
325
- )
319
+ val defaultViewHandlerInfo = if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
320
+ packageManager.resolveActivity(
321
+ activityIntent,
322
+ PackageManager .ResolveInfoFlags .of(MATCH_DEFAULT_ONLY .toLong())
323
+ )
324
+ } else {
325
+ @Suppress(" DEPRECATION" )
326
+ packageManager.resolveActivity(
327
+ activityIntent,
328
+ MATCH_DEFAULT_ONLY
329
+ )
330
+ }
326
331
Log .d(TAG , " [browserPackageName] Resolved activity info: $defaultViewHandlerInfo " )
327
332
var defaultViewHandlerPackageName: String? = null
328
333
if (defaultViewHandlerInfo != null ) {
@@ -331,14 +336,28 @@ open class AmplifyAuthCognitoPlugin :
331
336
Log .d(TAG , " [browserPackageName] Resolved default package: $defaultViewHandlerPackageName " )
332
337
333
338
// Get all apps that can handle VIEW intents.
334
- val resolvedActivityList = packageManager.queryIntentActivities(activityIntent, MATCH_ALL )
339
+ val resolvedActivityList = if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
340
+ packageManager.queryIntentActivities(
341
+ activityIntent,
342
+ PackageManager .ResolveInfoFlags .of(MATCH_ALL .toLong())
343
+ )
344
+ } else {
345
+ @Suppress(" DEPRECATION" )
346
+ packageManager.queryIntentActivities(activityIntent, MATCH_ALL )
347
+ }
335
348
val packagesSupportingCustomTabs = mutableListOf<String >()
336
349
for (info in resolvedActivityList) {
337
350
val serviceIntent = Intent ()
338
351
serviceIntent.action = ACTION_CUSTOM_TABS_CONNECTION
339
352
serviceIntent.`package` = info.activityInfo.packageName
340
353
// Check if the package also resolves the Custom Tabs service.
341
- if (packageManager.resolveService(serviceIntent, 0 ) != null ) {
354
+ val resolvedService = if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
355
+ packageManager.resolveService(serviceIntent, PackageManager .ResolveInfoFlags .of(0 ))
356
+ } else {
357
+ @Suppress(" DEPRECATION" )
358
+ packageManager.resolveService(serviceIntent, 0 )
359
+ }
360
+ if (resolvedService != null ) {
342
361
packagesSupportingCustomTabs.add(info.activityInfo.packageName)
343
362
}
344
363
}
@@ -461,6 +480,12 @@ open class AmplifyAuthCognitoPlugin :
461
480
}
462
481
true
463
482
}
483
+ } else if (intent.hasExtra(CUSTOM_TAB_CANCEL_EXTRA )) {
484
+ // Intents originating with this extra are meant to close the loop on sign-in/out events.
485
+ // See the notes on [onActivityResult] for more information.
486
+ Log .d(TAG , " [onNewIntent] Cancelling current operation" )
487
+ cancelCurrentOperation()
488
+ return true
464
489
}
465
490
Log .d(TAG , " [onNewIntent] Not handling intent" )
466
491
return false
@@ -469,20 +494,28 @@ open class AmplifyAuthCognitoPlugin :
469
494
/* *
470
495
* Called when the custom tab activity completes, either by cancellation or success.
471
496
*
472
- * There is no way to distinguish whether it is a success or cancellation, but since this is
473
- * called just before or after [onNewIntent], we wait a second for both to be called.
474
- * Afterwards, we are guaranteed that if [signInResult]/[signOutResult] is not `null`, then it
475
- * is a cancellation since otherwise, [handleSignInResult] would have set it to `null`.
497
+ * There is no way to distinguish whether it is a success or cancellation, and our logic for
498
+ * handling the success case is centered in [onNewIntent] which is called just before or after
499
+ * this method in the success case and not at all in the error case.
500
+ *
501
+ * In the cancellation case, [onNewIntent] is not called, only this method, which means that
502
+ * we either need to wait for a couple seconds to see if [onNewIntent] is called or trigger an
503
+ * intent of our own which will be intercepted by [onNewIntent]. The former is flaky and found to
504
+ * be unreliable, but the latter is robust.
505
+ *
506
+ * By the time we reach this method, a new intent will have been initiated in the success case.
507
+ * By creating a new one, we are guaranteed it will be handled after the success intent, in which
508
+ * case it's a no-op. In the error case, the intent we create will trigger the cancellation of
509
+ * the pending sign-in or sign-out request. Either way, the request is completed.
476
510
*/
477
511
override fun onActivityResult (requestCode : Int , resultCode : Int , intent : Intent ? ): Boolean {
478
512
Log .d(TAG , " [onActivityResult] Got result: requestCode=$requestCode , resultCode=$resultCode , intent=$intent " )
479
513
if (requestCode == CUSTOM_TAB_REQUEST_CODE ) {
480
- scope.launch {
481
- delay(1000L )
482
- launch(Dispatchers .Main ) {
483
- cancelCurrentOperation()
484
- }
514
+ val cancelIntent = Intent (applicationContext!! , mainActivity!! .javaClass).apply {
515
+ addFlags(Intent .FLAG_ACTIVITY_NEW_TASK )
516
+ putExtra(CUSTOM_TAB_CANCEL_EXTRA , true )
485
517
}
518
+ applicationContext!! .startActivity(cancelIntent)
486
519
return true
487
520
}
488
521
return false
0 commit comments