Skip to content

[BottomTabsCustomRow] Fix crash attaching custom row to non-AppCompat activities - #8337

Merged
yedidyak merged 1 commit into
masterfrom
fix_custom_row_attacher_appcompat_crash
Jul 30, 2026
Merged

[BottomTabsCustomRow] Fix crash attaching custom row to non-AppCompat activities#8337
yedidyak merged 1 commit into
masterfrom
fix_custom_row_attacher_appcompat_crash

Conversation

@avithalker-wix

Copy link
Copy Markdown
Contributor

Problem

BottomTabsCustomRowAttacher is registered as process-wide Application.ActivityLifecycleCallbacks, so tryAttach() runs for every activity in the host app (from onActivityCreated, onActivityStarted, onActivityResumed, registerOnce and rescan).

tryAttach() resolved the overlay host with Activity.findViewById(android.R.id.content). On an AppCompat activity that call routes through AppCompatDelegateImpl.findViewById(), which calls ensureSubDecor() -> createSubDecor(). createSubDecor() validates the theme and throws:

java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
  at androidx.appcompat.app.AppCompatDelegateImpl.createSubDecor(AppCompatDelegateImpl.java:902)
  at androidx.appcompat.app.AppCompatActivity.findViewById(AppCompatActivity.java:264)
  at com.reactnativenavigation.customrow.BottomTabsCustomRowAttacher.tryAttach(BottomTabsCustomRowAttacher.kt:96)
  at com.reactnativenavigation.customrow.BottomTabsCustomRowAttacher.onActivityCreated(BottomTabsCustomRowAttacher.kt:55)
  at android.app.Application.dispatchActivityCreated(Application.java:368)

The exception escapes onActivityCreated, so performLaunchActivity() raises a RuntimeException and Android force-finishes the activity. There is no try/catch on the path.

Any activity in the process that inherits a theme which is not a Theme.AppCompat descendant therefore crashes on creation, even though it has nothing to do with bottom tabs. This is easy to hit with activities that declare no android:theme of their own and so inherit an application theme such as Theme.App.Starting from androidx.core:core-splashscreen.

A concrete case: AppAuth's net.openid.appauth.RedirectUriReceiverActivity (an invisible activity that receives the OAuth redirect and declares no theme). It is force-finished before it can hand the intent to AuthorizationManagementActivity, so the authorization code is dropped and authorize() never resolves - breaking OAuth/OIDC sign-in entirely on Android.

Solution

  • Resolve android.R.id.content from the decor view (scanRoot.findViewById(...)) instead of via the activity. View.findViewById() never engages AppCompatDelegate, so no sub-decor is created and no theme validation runs.
  • Drop the now-redundant activity.findViewById() fallback used to derive scanRoot: it carried the same hazard, and if decorView is unavailable there is no hierarchy to scan or attach to.

Behaviour is unchanged for activities with a compatible theme - Activity.findViewById() already delegates to getWindow().getDecorView().findViewById(), so the resolved view is the same. On activities where setContentView() has not run yet, the lookup now returns null and falls back to scanRoot rather than eagerly building the AppCompat sub-decor as a side effect; those activities have no BottomTabs children yet, so forEachBottomTabs is a no-op and later onActivityStarted / onActivityResumed / layout-observer passes attach as before.

Worth considering as a follow-up (not included here, to keep this fix minimal): skipping non-RNN-hosted activities outright, since BottomTabs can only exist in an RNN activity, and ensureLayoutObserver currently installs a global layout listener on every activity in the process.

Extra information (Jira ticket, design doc, Slack thread, etc..)

BottomTabsCustomRowAttacher is registered process-wide, so tryAttach() runs
for every activity in the app. It called Activity.findViewById(), which on an
AppCompat activity routes through AppCompatDelegateImpl and forces
createSubDecor() -- that throws IllegalStateException unless the activity's
theme derives from Theme.AppCompat.

Any activity inheriting a non-AppCompat theme therefore crashed on creation.
Resolve android.R.id.content through the decor view instead, which never
engages AppCompatDelegate. Activity.findViewById() already delegates to
getWindow().getDecorView().findViewById(), so behaviour is unchanged for
activities that do have a compatible theme.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@yedidyak
yedidyak enabled auto-merge (squash) July 30, 2026 08:16
@yedidyak
yedidyak disabled auto-merge July 30, 2026 08:16
@yedidyak
yedidyak merged commit a563e9d into master Jul 30, 2026
2 checks passed
yedidyak added a commit to Yoavpagir/react-native-navigation that referenced this pull request Jul 30, 2026
…ties

Resolves the overlap with wix#8337 (merged as a563e9d), which fixed the same
Theme.AppCompat crash by resolving android.R.id.content through the decor
view instead of Activity.findViewById().

Kept both layers: master's safe lookup (removes the AppCompatDelegate
landmine) plus this branch's NavigationActivity guard (also stops the
global layout observer and decor-tree walk from running on every activity
in the process). Net change vs master is now just the guard.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants