fix: restore Android sign-in by routing OAuth callback through a trampoline activity#3091
fix: restore Android sign-in by routing OAuth callback through a trampoline activity#3091
Conversation
There was a problem hiding this comment.
Pull request overview
This PR aims to fix Android OAuth sign-in by routing the org.lichess.mobile://login-callback deep link through a dedicated trampoline activity instead of letting MainActivity receive the redirect directly. In the codebase, this change is intended to make the app-links/OAuth callback flow more reliable while preserving the existing Flutter MainActivity instance.
Changes:
- Added a new
OAuthCallbackActivityto receive the OAuth redirect URI and forward it toMainActivity. - Removed the OAuth callback intent filter from
MainActivity. - Declared the new callback activity in the Android manifest as the sole handler for
org.lichess.mobile://login-callback.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
android/app/src/main/kotlin/org/lichess/mobileV2/OAuthCallbackActivity.kt |
Adds the Android trampoline activity that forwards the login callback to MainActivity. |
android/app/src/main/AndroidManifest.xml |
Rewires deep-link registration so the OAuth callback is handled by the new activity instead of MainActivity. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Chrome Custom Tabs fires the redirect intent from its own process/task, and the default taskAffinity rules mean OAuthCallbackActivity ends up in Chrome's task. When it then tries FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP, Android only searches the current task — MainActivity isn't there, so a new instance is created instead of onNewIntent being called. The fix is to add FLAG_ACTIVITY_NEW_TASK to the forwarding intent. This tells Android to find the existing task whose affinity matches MainActivity (the app's task), bring it to the foreground, then apply CLEAR_TOP | SINGLE_TOP within that task to call onNewIntent on the existing MainActivity.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Problem
android:launchMode="singleTop"was restored in #3080. WithsingleTop, when Chrome Custom Tabredirects to
org.lichess.mobile://login-callback, Android creates a newMainActivityinstance (because the existing one is not at the top of the task stack — the Custom Tab is).
That new instance gets a new Flutter engine and a separate Dart isolate, so the OAuth callback URI
never reaches the
callbackCompleterwaiting in the originalsignIn()call. The app lifecycle'sonResumefires, the 300 ms cancellation guard trips, and sign-in silently fails.Fix
Introduce
OAuthCallbackActivity, a minimal trampoline with no UI (Theme.NoDisplay) that ownsthe
org.lichess.mobile://login-callbackintent filter instead ofMainActivity. When theredirect fires it immediately forwards the URI to the existing
MainActivityvia:FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP
NEW_TASKcrosses the task boundary if Chrome launched the trampoline in its own taskCLEAR_TOPfinds the existingMainActivityin the app's taskSINGLE_TOPcallsonNewIntenton it rather than recreating itonNewIntentis delivered beforeonResume, soapp_linksroutes the URI throughuriLinkStreamand the
callbackCompletercompletes successfully before the cancellation timer can fire.MainActivitystayssingleTop, preserving correct back-navigation when opening the app fromexternal intents (e.g. file manager).