ADFA-3598: Move jetpack compose preview into a plugin#1461
Conversation
Signed-off-by: Akash Yadav <itsaky01@gmail.com>
# Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
# Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
Add the host-side contract an external .cgp plugin needs to host the Jetpack Compose preview feature independently of the app: - IdeServices: add ModuleContext data class plus default-body IdeProjectService.getModuleContext(filePath) and IdeBuildService.executeTasks(vararg tasks) (binary-compatible additions via interface default methods) - ModuleContextResolver: resolve a module's compile/runtime classpaths, resource APK and build state via IProjectManager / AndroidModule - IdeProjectServiceImpl: implement getModuleContext over the resolver - IdeBuildServiceImpl: bridge executeTasks to the host BuildService
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
This reverts commit e4fd087.
📝 Walkthrough
Risks / best-practice notes:
WalkthroughAdds plugin toolbar action support, new plugin service APIs for module context and task execution, and removes compose-preview build wiring while limiting preview actions to XML layouts. ChangesPlugin Toolbar Action System
Plugin Services API Extension
Compose Preview Removal and XML-only Preview
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (1)
profiler/build.gradle.kts (1)
8-10: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winWire
profiler/proguard-rules.prointo the module config.
This file is currently unused; addconsumerProguardFiles("proguard-rules.pro")if the library should publish keep rules, or remove it until needed.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@profiler/build.gradle.kts` around lines 8 - 10, The profiler module currently defines android { namespace = "${BuildConfig.PACKAGE_NAME}.profiler" } but does not use profiler/proguard-rules.pro. Update the profiler module configuration in build.gradle.kts to either wire in consumerProguardFiles("proguard-rules.pro") if the library should ship keep rules, or remove the unused proguard file if it is not needed. Use the android block and the profiler module’s Gradle config as the place to make the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/main/java/com/itsaky/androidide/actions/etc/PreviewLayoutAction.kt`:
- Line 149: The preview base path in PreviewLayoutAction should not be derived
with substringBefore("layout"), since it can truncate earlier path segments in
directories that merely contain that word. Update the intent extra setup to
compute the prefix from the actual resource directory boundary in
PreviewLayoutAction (for example, using the layout folder segment or parent
directories) before passing Constants.EXTRA_KEY_FILE_PATH to EditorActivity.
- Around line 82-86: The probe in PreviewLayoutAction’s path parsing is catching
Throwable too broadly, which hides unexpected failures. Update the try/catch
around extractPathData(file) to catch only the specific parse exception it can
legitimately throw, and in that handler log or report the failure before calling
markInvisible(). Keep the fallback behavior in PreviewLayoutAction, but avoid
swallowing Errors or unrelated bugs.
In `@app/src/main/java/com/itsaky/androidide/utils/EditorActivityActions.kt`:
- Around line 190-194: The toolbar actions registered in EditorActivityActions
for PluginToolbarActionItem are not being rebuilt when a plugin is disabled, so
stale toolbar callbacks can remain active. Update
EditorHandlerActivity.tearDownDisabledPluginContributions() to clear and
re-register EDITOR_TOOLBAR actions from the currently enabled plugins, mirroring
the existing sidebar rebuild logic before invalidateOptionsMenu(). Use the
existing plugin action registration flow around plugin.getToolbarActions() and
registry.registerAction(...) so disabled plugins no longer leave behind toolbar
items.
In
`@plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/extensions/UIExtension.kt`:
- Around line 141-155: ToolbarAction currently stores isEnabledProvider and
isVisibleProvider as mutable body properties, so data-class features like
copy(), equals(), and destructuring ignore them and copied actions lose the
dynamic callbacks. Move both provider callbacks into the primary constructor of
ToolbarAction and update any call sites/defaults so the data class includes them
in generated behavior while preserving the existing static isEnabled and
isVisible fallback logic.
In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeBuildServiceImpl.kt`:
- Around line 51-60: Gate the IdeBuildServiceImpl.executeTasks entry point
behind plugin-scoped authorization before forwarding to
BuildService.executeTasks. Add a permission check or a permission-aware wrapper
using the plugin identity available at the call site, and reject unauthorized
callers before any task execution is attempted. Keep the change localized to
executeTasks in IdeBuildServiceImpl and ensure only approved plugins can invoke
arbitrary Gradle tasks.
In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt`:
- Around line 84-94: getModuleContext() is missing the same host-path
authorization check used by getProjectByPath(), which allows probing disallowed
paths. Update IdeProjectServiceImpl.getModuleContext to validate the incoming
filePath with isPathAllowed() before calling ModuleContextResolver.resolve(),
and fail fast (for example with SecurityException) when the path is outside the
plugin’s allowed roots; keep the existing permission check and resolver flow
otherwise.
---
Nitpick comments:
In `@profiler/build.gradle.kts`:
- Around line 8-10: The profiler module currently defines android { namespace =
"${BuildConfig.PACKAGE_NAME}.profiler" } but does not use
profiler/proguard-rules.pro. Update the profiler module configuration in
build.gradle.kts to either wire in consumerProguardFiles("proguard-rules.pro")
if the library should ship keep rules, or remove the unused proguard file if it
is not needed. Use the android block and the profiler module’s Gradle config as
the place to make the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f80b052f-7faa-4500-ab06-8b0272dee1ce
📒 Files selected for processing (45)
actions/src/main/java/com/itsaky/androidide/actions/ActionItem.ktapp/build.gradle.ktsapp/src/main/java/com/itsaky/androidide/actions/PluginToolbarActionItem.ktapp/src/main/java/com/itsaky/androidide/actions/etc/PreviewLayoutAction.ktapp/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.ktapp/src/main/java/com/itsaky/androidide/utils/EditorActivityActions.ktcompose-preview/build.gradle.ktscompose-preview/src/main/AndroidManifest.xmlcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/ComposePreviewActivity.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/ComposePreviewFragment.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/ComposePreviewViewModel.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/compiler/CompilerDaemon.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/compiler/ComposeClasspathManager.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/compiler/ComposeCompiler.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/compiler/ComposeDexCompiler.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/compiler/DexCache.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/data/repository/ComposePreviewRepository.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/data/repository/ComposePreviewRepositoryImpl.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/data/source/ProjectContextSource.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/domain/PreviewSourceParser.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/domain/model/ParsedPreviewSource.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/runtime/ComposableInvoker.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/runtime/ComposableRenderer.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/runtime/ComposeClassLoader.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/runtime/ComposeSignature.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/runtime/ProjectResourceContextFactory.ktcompose-preview/src/main/java/com/itsaky/androidide/compose/preview/ui/BoundedComposeView.ktcompose-preview/src/main/res/drawable/ic_view_grid.xmlcompose-preview/src/main/res/drawable/ic_view_single.xmlcompose-preview/src/main/res/layout/activity_compose_preview.xmlcompose-preview/src/main/res/layout/fragment_compose_preview.xmlcompose-preview/src/main/res/layout/item_preview_card.xmlcompose-preview/src/main/res/menu/menu_compose_preview.xmlplugin-api/src/main/kotlin/com/itsaky/androidide/plugins/extensions/UIExtension.ktplugin-api/src/main/kotlin/com/itsaky/androidide/plugins/services/IdeServices.ktplugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeBuildServiceImpl.ktplugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.ktplugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/ModuleContextResolver.ktplugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/ui/PluginUiActionManager.ktprofiler/.gitignoreprofiler/build.gradle.ktsprofiler/consumer-rules.proprofiler/proguard-rules.proprofiler/src/main/AndroidManifest.xmlsettings.gradle.kts
💤 Files with no reviewable changes (28)
- compose-preview/src/main/res/layout/fragment_compose_preview.xml
- compose-preview/src/main/res/drawable/ic_view_grid.xml
- compose-preview/src/main/res/menu/menu_compose_preview.xml
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/ComposePreviewActivity.kt
- compose-preview/src/main/res/drawable/ic_view_single.xml
- compose-preview/src/main/res/layout/item_preview_card.xml
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/runtime/ComposeSignature.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/data/source/ProjectContextSource.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/domain/model/ParsedPreviewSource.kt
- compose-preview/src/main/res/layout/activity_compose_preview.xml
- compose-preview/src/main/AndroidManifest.xml
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/compiler/DexCache.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/compiler/ComposeCompiler.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/runtime/ProjectResourceContextFactory.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/runtime/ComposeClassLoader.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/compiler/CompilerDaemon.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/ui/BoundedComposeView.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/domain/PreviewSourceParser.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/data/repository/ComposePreviewRepositoryImpl.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/data/repository/ComposePreviewRepository.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/ComposePreviewFragment.kt
- compose-preview/build.gradle.kts
- app/build.gradle.kts
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/compiler/ComposeDexCompiler.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/runtime/ComposableRenderer.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/compiler/ComposeClasspathManager.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/runtime/ComposableInvoker.kt
- compose-preview/src/main/java/com/itsaky/androidide/compose/preview/ComposePreviewViewModel.kt
- PreviewLayoutAction: catch Exception (not Throwable) and log on resource-path
parse failure; derive the layout base path from the last "layout/" segment
instead of substringBefore("layout")
- EditorHandlerActivity: rebuild EDITOR_TOOLBAR plugin actions on plugin disable
so stale PluginToolbarActionItem callbacks are cleared
- IdeProjectServiceImpl: path-validate getModuleContext via isPathAllowed()
- ToolbarAction: document why the provider callbacks stay body vars (ABI compat)
Deferred: permission-gating IdeBuildService.executeTasks needs a per-plugin
permission-aware wrapper + a new build permission (build service is a shared
singleton); to be done as a focused security change.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt`:
- Around line 144-147: Keep the project-root lookups independent in
IdeProjectServiceImpl so a failure in getCurrentProject() does not prevent
getAllProjects() from being processed. Refactor the current runCatching block
around projectProvider.getCurrentProject() and projectProvider.getAllProjects()
so each call is handled separately, preserving roots collection even if one
source throws. Ensure the roots set is still populated from both sources using
the existing projectProvider and roots symbols.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f80c6ae3-bcb7-4eba-bc3f-0fb2a080a713
📒 Files selected for processing (4)
app/src/main/java/com/itsaky/androidide/actions/etc/PreviewLayoutAction.ktapp/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.ktplugin-api/src/main/kotlin/com/itsaky/androidide/plugins/extensions/UIExtension.ktplugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt
🚧 Files skipped from review as they are similar to previous changes (2)
- plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/extensions/UIExtension.kt
- app/src/main/java/com/itsaky/androidide/actions/etc/PreviewLayoutAction.kt
No description provided.