From 887b2d97ff2cbb57ea39218ba456dbefeb2ab407 Mon Sep 17 00:00:00 2001 From: Hal Eisen Date: Wed, 18 Mar 2026 20:16:31 -0700 Subject: [PATCH 1/2] Guard against IllegalStateException when BuildOutputFragment.clearOutput() is called after the fragment is detached --- .../fragments/output/BuildOutputFragment.kt | 8 ++++++++ .../fragments/output/ShareableOutputFragment.kt | 8 +++++++- .../com/itsaky/androidide/ui/EditorBottomSheet.kt | 11 ++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt index 98116433f8..15d45dbadd 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt +++ b/app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt @@ -91,6 +91,14 @@ class BuildOutputFragment : NonEditableEditorFragment() { } override fun clearOutput() { + // This fragment uses `activityViewModels()`, which relies on the fragment being attached. + // `clearOutput()` can be triggered from build/service callbacks even after the fragment + // has been detached, so we must guard before touching the activity-scoped ViewModel. + if (!isAdded || isDetached) { + super.clearOutput() + return + } + buildOutputViewModel.clear() super.clearOutput() } diff --git a/app/src/main/java/com/itsaky/androidide/fragments/output/ShareableOutputFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/output/ShareableOutputFragment.kt index fd12a850b4..45de634d35 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/output/ShareableOutputFragment.kt +++ b/app/src/main/java/com/itsaky/androidide/fragments/output/ShareableOutputFragment.kt @@ -29,6 +29,12 @@ interface ShareableOutputFragment { /** Get the name of the file to which the output will be written. */ fun getShareableFilename(): String - /** Clear the output of this fragment. */ + /** + * Clear the output of this fragment. + * + * Note: Callers may invoke this while the fragment is detached (for example, from a + * build/service callback). Implementations that use activity-scoped ViewModels must + * guard against `!isAdded || isDetached` before touching them. + */ fun clearOutput() } diff --git a/app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt b/app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt index f912b5b68a..4c2610b058 100644 --- a/app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt +++ b/app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt @@ -430,7 +430,16 @@ constructor( suppressedGradleWarnings.any { msg.contains(it) } fun clearBuildOutput() { - pagerAdapter.buildOutputFragment?.clearOutput() + // Always clear the persistent build output state via the bottom sheet's + // activity-scoped ViewModel. + buildOutputViewModel.clear() + + // `clearOutput()` on the fragment may touch additional UI state, so only call it when + // the fragment is currently attached to an activity. + val fragment = pagerAdapter.buildOutputFragment + if (fragment != null && fragment.isAdded && !fragment.isDetached) { + fragment.clearOutput() + } } fun handleDiagnosticsResultVisibility(errorVisible: Boolean) { From 92abe7a64b2e7e1b5b053b4c8516598b85f010e4 Mon Sep 17 00:00:00 2001 From: Hal Eisen Date: Wed, 18 Mar 2026 22:02:50 -0700 Subject: [PATCH 2/2] Simplify BuildOutputFragment.clearOutput() --- .../androidide/fragments/output/BuildOutputFragment.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt index 15d45dbadd..f12311e0e0 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt +++ b/app/src/main/java/com/itsaky/androidide/fragments/output/BuildOutputFragment.kt @@ -94,12 +94,9 @@ class BuildOutputFragment : NonEditableEditorFragment() { // This fragment uses `activityViewModels()`, which relies on the fragment being attached. // `clearOutput()` can be triggered from build/service callbacks even after the fragment // has been detached, so we must guard before touching the activity-scoped ViewModel. - if (!isAdded || isDetached) { - super.clearOutput() - return + if (isAdded && !isDetached) { + buildOutputViewModel.clear() } - - buildOutputViewModel.clear() super.clearOutput() }