You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[$175] Android - Plaid login page rotates to landscape instead of remaining in portrait #106342
Version Number: v9.5.34-0 Reproducible in staging?: Yes Reproducible in production?: Yes If this was caught during regression testing, add the test name, ID and link from BrowserStack:#105598 Email or phone of affected tester (no customers):setowskiw+70@gmail.com Issue reported by: Applause Internal Team Bug source: Pull Request QA execution Device used: Samsung Galaxy S21 FE/Android 16 App Component: Other
Action Performed:
Launch Expensify app
Tap Profile
Tap Wallet
Tap Add bank account, select United States and tap Regions Bank
Tap the Online ID field
Rotate the device to landscape
Exit Plaid and reopen it while the device is in landscape
Expected Result:
Plaid remains in portrait orientation on Android phones, including when reopened while the device is in landscape.
Actual Result:
Plaid rotates to landscape along with the device. When reopened in landscape, Plaid also displays in landscape instead of portrait.
changed the title [-]Android - Plaid login page rotates to landscape instead of remaining in portrait[/-][+][$175] Android - Plaid login page rotates to landscape instead of remaining in portrait[/+]on Nov 9, 2026
7. Reopen Plaid while landscape; Plaid opens portraitCannot verify native Android reopening behavior on web.
⚠️
Issue Analysis
⚠️Preliminary analysis — root cause remains unconfirmed.
Finding: The reported-version App source already explicitly locks Plaid to portrait on phones. Adding another portrait request is therefore not a verified solution. The unresolved boundary is whether the tested native build executes that policy and whether its orientation request is subsequently overridden.
Code evidence and hypothesis checks
In the reported staging tag, MainApplication.kt:70–83 recognizes Plaid’s activity and applies the orientation policy when it opens.
MainApplication.kt:126–142 reapplies the policy on configuration changes: below 600dp, it requests portrait; at or above 600dp, it permits rotation. The Android 8.0 exception does not explain this Android 16 report.
Alternative explanations remain unverified: the shipped policy is not executed, the device enters the ≥600dp branch, or the SDK/platform overrides the request. Android 16 alone does not establish which occurred.
Recommended next investigation: An internal engineer should inspect the tested Android build and capture the active Plaid activity, effective screen-width classification, and requested orientation during opening, rotation, and reopening. This distinguishes the remaining explanations before selecting a fix. Backend logs would not establish those native activity states.
In android/app/src/main/java/com/expensify/chat/MainApplication.kt: applyPlaidOrientation is currently only invoked inside onActivityCreated (and in Application.onConfigurationChanged if the configuration changes while Plaid is alive).
However:
LinkActivity in AndroidManifest.xml is declared with android:launchMode="singleTask":
When a user exits Plaid, rotates the device to landscape, and re-opens Plaid from Expensify, LinkActivity is restarted or resumed from the task stack (onRestart -> onStart -> onResume). Because the activity was already instantiated, onActivityCreatednever fires again.
Because onActivityStarted and onActivityResumed in MainApplication.kt are currently empty no-op stubs (override fun onActivityStarted(activity: Activity) {}), applyPlaidOrientation is never re-applied when Plaid is reopened while the host app is in landscape.
Furthermore, if plaidLinkActivity was cleared or not refreshed, Application.onConfigurationChanged cannot re-assert portrait lock on the active instance. Consequently, Plaid inherits the landscape window orientation, causing the keyboard to obscure the login inputs on phones.
What changes do you think we should make in order to solve the problem?
In android/app/src/main/java/com/expensify/chat/MainApplication.kt:
In registerActivityLifecycleCallbacks:
Store/refresh the plaidLinkActivity weak reference and invoke applyPlaidOrientation in onActivityStarted and onActivityResumed as well as onActivityCreated:
Ensure the portrait lock is strictly enforced on all phones (config.smallestScreenWidthDp < 600) whenever the activity is active, started, or resumed, while preserving landscape for tablets:
privatefunapplyPlaidOrientation(activity:Activity, config:Configuration) {
if (config.smallestScreenWidthDp >=600) {
activity.requestedOrientation =ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIEDreturn
}
// Android 8.0 throws when a translucent activity requests a fixed orientation; Plaid skips it there too.if (Build.VERSION.SDK_INT!=Build.VERSION_CODES.O) {
activity.requestedOrientation =ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
}
HybridApp parity note: Since native activity lifecycle callbacks in MainApplication.kt mirror across NewDot and Mobile-Expensify, this fix applies cleanly to both standalone and hybrid builds without introducing regression risks to non-Plaid activities.
What alternative solutions did you explore? (Optional)
Attempting to lock orientation from the React Native JS layer via orientation hooks was considered. However, Plaid's LinkActivity runs in a native Android activity layer outside the React Native view hierarchy. Centralizing the lifecycle listener inside MainApplication.kt's registerActivityLifecycleCallbacks (onActivityStarted & onActivityResumed) ensures Plaid is unconditionally locked to portrait on Android phones across all open, reopen, resume, and rotation transitions.
Tests
On an Android phone, launch the Expensify app.
Tap Profile > Wallet.
Tap Add bank account, select United States, and tap Regions Bank.
On the Plaid login screen, tap the Online ID field and rotate the device to landscape.
Verify Plaid remains in portrait orientation and the Online ID field remains clearly visible above the keyboard.
Tap back to exit Plaid, rotate the device to landscape.
Reopen Plaid while the device is in landscape.
Verify Plaid opens and remains in portrait orientation without rotating to landscape or showing a white page.
On an Android tablet, repeat steps 1–4 and verify Plaid can rotate to landscape as expected.
Please re-state the problem that we are trying to solve in this issue.
On Android, opening the Plaid bank authentication webview/activity allows the screen to rotate to landscape orientation instead of remaining locked in portrait mode.
What is the root cause of that problem?
The Android activity configuration launched for Plaid Link (PlaidLinkActivity or external auth browser activity) does not specify android:screenOrientation="portrait" in AndroidManifest.xml, allowing system auto-rotation settings to override the app's portrait lock during banking flows.
What changes do you think we should make in order to solve the problem?
In android/app/src/main/AndroidManifest.xml, ensure screenOrientation="portrait" is explicitly defined for all Plaid/OAuth activities:
In src/libs/Plaid/index.android.ts, enforce ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT) prior to opening the Plaid Link dialog.
What alternative solutions did you explore? (Optional)
Allowing landscape for Plaid: Rejected because Plaid's UI components and Expensify's parent modal are designed specifically for portrait, causing truncated form elements in landscape.
What automated tests can you add to prevent this from re-occurring?
In unit/integration tests:
Add a test verifying that launching the bank connection flow invokes lockAsync with PORTRAIT lock.
Please re-state the problem that we are trying to solve in this issue.
On Android phones:
When opening Plaid bank login (Profile > Wallet > Add bank account > United States > [Bank]), focusing the credential input, and rotating the device to landscape, Plaid rotates to landscape along with the device.
Exiting Plaid, rotating the device to landscape, and reopening Plaid from Expensify while in landscape results in Plaid displaying in landscape instead of remaining locked in portrait.
Missing orientation enforcement on resume / restart (singleTask lifecycle):
In android/app/src/main/AndroidManifest.xml:167-172, LinkActivity is declared with android:launchMode="singleTask" and android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode":
When the user exits Plaid, the activity is stopped rather than destroyed. When Plaid is reopened from the host app while the device is in landscape, Android does not re-create the activity; it delivers an intent and runs onRestart() -> onStart() -> onResume().
Because MainApplication.kt only calls applyPlaidOrientation in onActivityCreated—leaving onActivityStarted and onActivityResumed as empty stubs:
onActivityCreated never fires on reopen, and applyPlaidOrientation is never re-applied. LinkActivity therefore inherits the landscape window configuration of the parent task.
Plaid OAuth / WebView configuration changes:
Because LinkActivity handles orientation|screenSize in android:configChanges, rotating the device while Plaid is open (e.g. on Regions Bank OAuth / credential webview) dispatches onConfigurationChanged directly to LinkActivity. If plaidLinkActivity was not refreshed upon resume or if the webview resets the orientation request upon loading, applyPlaidOrientation must be re-asserted throughout the activity's active lifecycle states (started and resumed) so the window manager consistently locks the orientation to portrait on phones.
What changes do you think we should make in order to solve the problem?
In onActivityStarted and onActivityResumed, check if the activity is "com.plaid.internal.link.LinkActivity", refresh plaidLinkActivity = WeakReference(activity), and invoke applyPlaidOrientation(activity, activity.resources.configuration):
Ensure applyPlaidOrientation maintains tablet landscape support:
Keep the tablet check (config.smallestScreenWidthDp >= 600 -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) so tablet users continue to have full landscape support per PR Keep Plaid in portrait on Android phones so its inputs stay visible #105598, while locking phones (smallestScreenWidthDp < 600) to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT (with the Android 8.0 Build.VERSION.SDK_INT != Build.VERSION_CODES.O guard):
privatefunapplyPlaidOrientation(activity:Activity, config:Configuration) {
if (config.smallestScreenWidthDp >=600) {
activity.requestedOrientation =ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIEDreturn
}
if (Build.VERSION.SDK_INT!=Build.VERSION_CODES.O) {
activity.requestedOrientation =ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
}
What alternative solutions did you explore? (Optional)
Hardcoding android:screenOrientation="portrait" on LinkActivity in AndroidManifest.xml:
Locking orientation from React Native JS via ScreenOrientation.lockAsync:
Rejected because Plaid's LinkActivity is a native Android activity launched outside the React Native view hierarchy; JS-level orientation locks only control MainActivity (ReactActivity) and do not reliably govern Plaid's window manager parameters.
Tests
On an Android phone:
Launch the Expensify app.
Navigate to Profile > Wallet > Add bank account > United States > Regions Bank.
On the Plaid credential screen, tap the Online ID field and rotate the device to landscape.
Verify Plaid remains in portrait orientation and the Online ID field remains clearly visible above the keyboard.
Exit Plaid, rotate the device to landscape, and tap "Log into your bank" to reopen Plaid while the app is in landscape.
Verify Plaid opens and remains locked in portrait orientation without rotating to landscape or rendering a blank page.
On an Android tablet:
Repeat the bank connection flow and verify Plaid can freely rotate to landscape as expected.
— submitted with Antigravity on the contributor's behalf
Hey @hoangzinh, I checked the Mobile-Expensify pins for 9.5.34-0 and 9.5.36-0. Both predate the merge of Expensify/Mobile-Expensify#14216. Could you confirm which Android HybridApp build includes that fix so QA can retest on the Galaxy S21 FE with Android 16? Thanks.
Activity
applause-bot commentedon Nov 9, 2026
While Applause doesn't embed attachments for this bug, here are the links for quick view:
https://platform.applause.com/services/links/v1/external/5a2583566b4a5ae52084ad10f996cc97d5effc40e1ac7d7b4179dd4ca014b731
melvin-bot commentedon Nov 9, 2026
Triggered auto assignment to Contributor-plus team member for initial proposal review - @brunovjk (
External)[-]Android - Plaid login page rotates to landscape instead of remaining in portrait[/-][+][$175] Android - Plaid login page rotates to landscape instead of remaining in portrait[/+]melvin-bot commentedon Nov 9, 2026
Job added to Upwork: https://www.upwork.com/jobs/~022119666681731312773
MelvinBot commentedon Nov 9, 2026
🤖 Web test steps verification results:⚠️ partial
Verified app, Profile, and Wallet on web. An emailed security-code gate blocked bank selection; Android orientation cannot be validated on web.
1. Launch app
Home snapshot confirmed the app was running.2. Open Profile
Snapshot confirmed Profile.3. Open Wallet
Snapshot confirmed Wallet and Add bank account.4. Add bank account → United States → Regions Bank
Account validation required an emailed security code. Country and bank selection were not reached.5. Tap Online ID
Blocked by account validation; the field was not reached.6. Rotate Android phone; Plaid remains portrait
Cannot verify native Android orientation on web.7. Reopen Plaid while landscape; Plaid opens portrait
Cannot verify native Android reopening behavior on web.Issue Analysis
Finding: The reported-version App source already explicitly locks Plaid to portrait on phones. Adding another portrait request is therefore not a verified solution. The unresolved boundary is whether the tested native build executes that policy and whether its orientation request is subsequently overridden.
Code evidence and hypothesis checks
MainApplication.kt:70–83recognizes Plaid’s activity and applies the orientation policy when it opens.MainApplication.kt:126–142reapplies the policy on configuration changes: below 600dp, it requests portrait; at or above 600dp, it permits rotation. The Android 8.0 exception does not explain this Android 16 report.Recommended next investigation: An internal engineer should inspect the tested Android build and capture the active Plaid activity, effective screen-width classification, and requested orientation during opening, rotation, and reopening. This distinguishes the remaining explanations before selecting a fix. Backend logs would not establish those native activity states.
Related issue: Wallet—in landscape, input hidden and cursor shifts right after typing tracks the underlying landscape-input problem this portrait restriction intended to address.
Next Steps: Reply with
@MelvinBot <your feedback>to refine this analysis.view run
kevinjosh10 commentedon Nov 9, 2026
Proposal
What is the root cause of that problem?
In
android/app/src/main/java/com/expensify/chat/MainApplication.kt:applyPlaidOrientationis currently only invoked insideonActivityCreated(and inApplication.onConfigurationChangedif the configuration changes while Plaid is alive).However:
LinkActivityinAndroidManifest.xmlis declared withandroid:launchMode="singleTask":LinkActivityis restarted or resumed from the task stack (onRestart->onStart->onResume). Because the activity was already instantiated,onActivityCreatednever fires again.onActivityStartedandonActivityResumedinMainApplication.ktare currently empty no-op stubs (override fun onActivityStarted(activity: Activity) {}),applyPlaidOrientationis never re-applied when Plaid is reopened while the host app is in landscape.plaidLinkActivitywas cleared or not refreshed,Application.onConfigurationChangedcannot re-assert portrait lock on the active instance. Consequently, Plaid inherits the landscape window orientation, causing the keyboard to obscure the login inputs on phones.What changes do you think we should make in order to solve the problem?
In
android/app/src/main/java/com/expensify/chat/MainApplication.kt:registerActivityLifecycleCallbacks:plaidLinkActivityweak reference and invokeapplyPlaidOrientationinonActivityStartedandonActivityResumedas well asonActivityCreated:applyPlaidOrientation:config.smallestScreenWidthDp < 600) whenever the activity is active, started, or resumed, while preserving landscape for tablets:MainApplication.ktmirror across NewDot and Mobile-Expensify, this fix applies cleanly to both standalone and hybrid builds without introducing regression risks to non-Plaid activities.What alternative solutions did you explore? (Optional)
Attempting to lock orientation from the React Native JS layer via orientation hooks was considered. However, Plaid's
LinkActivityruns in a native Android activity layer outside the React Native view hierarchy. Centralizing the lifecycle listener insideMainApplication.kt'sregisterActivityLifecycleCallbacks(onActivityStarted&onActivityResumed) ensures Plaid is unconditionally locked to portrait on Android phones across all open, reopen, resume, and rotation transitions.Tests
TassioSales commentedon Nov 9, 2026
Contributor details
Your Expensify account email: tassiolucian.ljs@gmail.com
Upwork Profile Link: https://www.upwork.com/freelancers/~0179cdc82689f205c7
Proposal
Please re-state the problem that we are trying to solve in this issue.
On Android, opening the Plaid bank authentication webview/activity allows the screen to rotate to landscape orientation instead of remaining locked in portrait mode.
What is the root cause of that problem?
The Android activity configuration launched for Plaid Link (
PlaidLinkActivityor external auth browser activity) does not specifyandroid:screenOrientation="portrait"inAndroidManifest.xml, allowing system auto-rotation settings to override the app's portrait lock during banking flows.What changes do you think we should make in order to solve the problem?
android/app/src/main/AndroidManifest.xml, ensurescreenOrientation="portrait"is explicitly defined for all Plaid/OAuth activities:src/libs/Plaid/index.android.ts, enforceScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT)prior to opening the Plaid Link dialog.What alternative solutions did you explore? (Optional)
What automated tests can you add to prevent this from re-occurring?
In unit/integration tests:
lockAsyncwithPORTRAITlock.melvin-bot commentedon Nov 9, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
HindsightWise commentedon Nov 10, 2026
Contributor details
Your Expensify account email: hindsightwise@gmail.com
Upwork Profile Link: https://www.upwork.com/freelancers/~013d295f05a578824b
Proposal
Please re-state the problem that we are trying to solve in this issue.
On Android phones:
Profile > Wallet > Add bank account > United States > [Bank]), focusing the credential input, and rotating the device to landscape, Plaid rotates to landscape along with the device.What is the root cause of that problem?
In
android/app/src/main/java/com/expensify/chat/MainApplication.kt:Missing orientation enforcement on resume / restart (
singleTasklifecycle):In
android/app/src/main/AndroidManifest.xml:167-172,LinkActivityis declared withandroid:launchMode="singleTask"andandroid:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode":When the user exits Plaid, the activity is stopped rather than destroyed. When Plaid is reopened from the host app while the device is in landscape, Android does not re-create the activity; it delivers an intent and runs
onRestart()->onStart()->onResume().Because
MainApplication.ktonly callsapplyPlaidOrientationinonActivityCreated—leavingonActivityStartedandonActivityResumedas empty stubs:onActivityCreatednever fires on reopen, andapplyPlaidOrientationis never re-applied.LinkActivitytherefore inherits the landscape window configuration of the parent task.Plaid OAuth / WebView configuration changes:
Because
LinkActivityhandlesorientation|screenSizeinandroid:configChanges, rotating the device while Plaid is open (e.g. on Regions Bank OAuth / credential webview) dispatchesonConfigurationChangeddirectly toLinkActivity. IfplaidLinkActivitywas not refreshed upon resume or if the webview resets the orientation request upon loading,applyPlaidOrientationmust be re-asserted throughout the activity's active lifecycle states (startedandresumed) so the window manager consistently locks the orientation to portrait on phones.What changes do you think we should make in order to solve the problem?
Update
registerActivityLifecycleCallbacksinandroid/app/src/main/java/com/expensify/chat/MainApplication.kt:onActivityStartedandonActivityResumed, check if the activity is"com.plaid.internal.link.LinkActivity", refreshplaidLinkActivity = WeakReference(activity), and invokeapplyPlaidOrientation(activity, activity.resources.configuration):onActivityDestroyed:Ensure
applyPlaidOrientationmaintains tablet landscape support:Keep the tablet check (
config.smallestScreenWidthDp >= 600 -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) so tablet users continue to have full landscape support per PR Keep Plaid in portrait on Android phones so its inputs stay visible #105598, while locking phones (smallestScreenWidthDp < 600) toActivityInfo.SCREEN_ORIENTATION_PORTRAIT(with the Android 8.0Build.VERSION.SDK_INT != Build.VERSION_CODES.Oguard):What alternative solutions did you explore? (Optional)
android:screenOrientation="portrait"onLinkActivityinAndroidManifest.xml:ScreenOrientation.lockAsync:LinkActivityis a native Android activity launched outside the React Native view hierarchy; JS-level orientation locks only controlMainActivity(ReactActivity) and do not reliably govern Plaid's window manager parameters.Tests
Profile > Wallet > Add bank account > United States > Regions Bank.— submitted with Antigravity on the contributor's behalf
melvin-bot commentedon Nov 10, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
brunovjk commentedon Nov 11, 2026
Hey @hoangzinh, I checked the Mobile-Expensify pins for 9.5.34-0 and 9.5.36-0. Both predate the merge of Expensify/Mobile-Expensify#14216. Could you confirm which Android HybridApp build includes that fix so QA can retest on the Galaxy S21 FE with Android 16? Thanks.
hoangzinh commentedon Nov 12, 2026
I think it's 9.5.37-0 https://github.com/Expensify/Mobile-Expensify/pull/14216#issuecomment-6818878089