Skip to content

Comments

ADFA-1718: Add Material 3 theme support and IdeThemeService for plugins #937

Merged
Daniel-ADFA merged 7 commits intostagefrom
ADFA-1718
Feb 17, 2026
Merged

ADFA-1718: Add Material 3 theme support and IdeThemeService for plugins #937
Daniel-ADFA merged 7 commits intostagefrom
ADFA-1718

Conversation

@Daniel-ADFA
Copy link
Contributor

Add Material 3 theme support and IdeThemeService for plugins

Plugins can now define a PluginTheme style extending Material 3 DayNight
to automatically match the IDE's light/dark mode. PluginResourceContext
dynamically resolves the plugin's theme via getIdentifier() and syncs
configuration for correct resource qualifier selection.

Changes:

  • Add IdeThemeService interface and implementation for programmatic
    dark mode queries
  • Add PluginTheme with BlueWave color overrides to keystore and APK
    analyzer plugins
  • Add day/night color resources for themed status indicators
  • Fix requireContext() bug in KeystoreGeneratorFragment — use view
    context for plugin resource resolution
  • Update layouts to use themed colorBackground and textColorSecondary

  Plugins can now define a PluginTheme style extending Material 3 DayNight
  to automatically match the IDE's light/dark mode. PluginResourceContext
  dynamically resolves the plugin's theme via getIdentifier() and syncs
  configuration for correct resource qualifier selection. Hardcoded status
  colors in KeystoreGeneratorFragment are replaced with themed color
  resources, and requireContext() calls are fixed to use the view's plugin
  context to avoid Resources.
  Define PluginTheme with BlueWave color overrides and day/night color
  resources matching the keystore plugin. Update the layout to use themed
  colorBackground and textColorSecondary attributes for proper light/dark
  mode rendering.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 5, 2026

📝 Walkthrough
  • Features

    • New public IdeThemeService API for plugins: isDarkMode(), addThemeChangeListener(), removeThemeChangeListener
    • IdeThemeServiceImpl: per-plugin implementation that tracks system night mode, notifies listeners, and exposes dispose() for cleanup
    • Material 3 DayNight–based plugin theming: plugins may define PluginTheme (Theme.Material3.DayNight.NoActionBar) so plugin UI follows IDE light/dark mode
    • Dynamic plugin theme resolution: PluginResourceContext detects UI mode changes, rebuilds plugin Resources/Theme at runtime, resolves PluginTheme if present, and falls back to platform Material themes
    • PluginResourceContext helpers: getPluginPackageInfo(), getResourceId(name,type), inflateLayout(...); constructor no longer forces a base theme to enable dynamic resolution
    • PluginResourceContext recreates plugin assets on configuration changes to ensure correct resource qualifier selection (day/night)
    • Simplified PluginFragment inflation to use plugin context inflater and ensure plugin theme is referenced before inflation
    • Themed color tokens, night variants, and PluginTheme styles added for APK Viewer and Keystore Generator plugins
    • UI updates in plugin layouts: use themed attributes (?android:attr/colorBackground, ?android:attr/textColorSecondary) and Material button styling
    • KeystoreGeneratorFragment: fixed requireContext() bug by using view context for plugin resource resolution; replaced hardcoded status colors with themed color resources via ContextCompat
  • Changes to existing behavior

    • PluginManager now registers IdeThemeService per-plugin (for contexts with and without resources) and disposes instances on plugin unload
    • PluginResourceContext no longer applies a fixed theme at construction; theme is chosen and rebuilt dynamically when UI mode changes
    • Fragments/layouts rely on plugin-provided theme attributes rather than hardcoded colors in several places
  • Risks & Best-Practice Considerations

    • ⚠️ Theme flicker / transient mismatch: dynamic recreation of plugin Resources/Theme on configuration changes can cause brief visual inconsistencies during mode transitions; test across device configurations and multi-window scenarios
    • ⚠️ Missing PluginTheme fallback: plugins that don’t ship PluginTheme will fall back to platform themes and may lose expected branding or attributes—encourage plugin authors to include PluginTheme
    • ⚠️ Resource loading edge cases: dynamic AssetManager/Resources creation and sourceDir handling must be validated for split APKs, file-based installs, and other packaging variants
    • ⚠️ Listener lifecycle & threading: ThemeChangeListener callbacks must be handled safely by listeners (marshal to main thread as needed) and removed on unload to avoid leaks
    • ⚠️ Cleanup responsibility: IdeThemeServiceImpl.dispose() is invoked on unload, but plugins must not retain references to plugin services/resources afterward to prevent memory leaks
    • ⚠️ Potential API fragility: configuration handling touches platform configuration logic—review for deprecated APIs and platform compatibility
  • Notes for reviewers / validation checklist

    • Confirm PluginManager registers and unregisters IdeThemeServiceImpl for each plugin and calls dispose() on unload
    • Validate PluginResourceContext.getResourceId(), inflateLayout(), and theme selection logic correctly resolve day/night resources and plugin-supplied attributes in both light and dark modes
    • Verify KeystoreGeneratorFragment fix (use of view context and ContextCompat color resolution) removes the previous requireContext() issue and displays themed status colors
    • Test end-to-end UI mode switching for APK Viewer and Keystore Generator plugins (including rotation and multi-window) to ensure no crashes, missing attributes, or transient inconsistencies
    • Validate resource resolution for different plugin packaging types (split APKs, file-based installs) to ensure dynamic AssetManager/Resources recreation finds plugin resources reliably

Walkthrough

Adds per‑plugin theming: new light/dark color resources and PluginTheme styles for two plugins, a theme service API and implementation (IdeThemeService + IdeThemeServiceImpl), dynamic plugin resource/theme resolution on UI mode changes, wiring of per‑plugin theme services in PluginManager, and UI updates to use theme colors.

Changes

Cohort / File(s) Summary
APK Viewer UI & Theme
apk-viewer-plugin/src/main/res/layout/fragment_sample.xml, apk-viewer-plugin/src/main/res/values/colors.xml, apk-viewer-plugin/src/main/res/values-night/colors.xml, apk-viewer-plugin/src/main/res/values/styles.xml
Add light/dark color resources and PluginTheme; apply theme background to ScrollView and style the Start button.
Keystore Generator UI & Theme
keystore-generator-plugin/src/main/res/layout/fragment_keystore_generator.xml, keystore-generator-plugin/src/main/res/values/colors.xml, keystore-generator-plugin/src/main/res/values-night/colors.xml, keystore-generator-plugin/src/main/res/values/styles.xml, keystore-generator-plugin/src/main/AndroidManifest.xml
Add light/dark color resources and PluginTheme; update Generate button textColor; bump plugin.version metadata.
Keystore Generator Fragment Logic
keystore-generator-plugin/src/main/kotlin/.../fragments/KeystoreGeneratorFragment.kt
Replace hardcoded/system colors with theme color lookups via ContextCompat.getColor(...) for status/progress/success/error UI states.
Plugin API: Inflater & Theme Service
plugin-api/src/main/kotlin/.../base/PluginFragment.kt, plugin-api/src/main/kotlin/.../services/IdeThemeService.kt
Simplify plugin inflater retrieval; add IdeThemeService and ThemeChangeListener interfaces for querying dark mode and registering listeners.
Plugin Manager: Theme Integration
plugin-manager/src/main/kotlin/.../core/PluginManager.kt, plugin-manager/src/main/kotlin/.../services/IdeThemeServiceImpl.kt
Register per‑plugin IdeThemeService instances using IdeThemeServiceImpl, manage lifecycle/dispose on unload.
Plugin Resource Context: Dynamic Theme Resolution
plugin-manager/src/main/kotlin/.../loaders/PluginResourceContext.kt
Make PluginResourceContext night‑mode aware: track UI mode, recreate plugin resources on config changes, resolve/apply PluginTheme or sensible fallbacks, cache theme, and add helpers (getPluginPackageInfo, getResourceId, inflateLayout).
Misc: Imports & Minor Layout Tweaks
plugin-api/src/main/kotlin/.../base/PluginFragment.kt, app/src/main/java/.../ThemeManager.kt, various layout files
Minor import cleanup/adaptations and small layout attribute additions (textColor/style attributes).

Sequence Diagram(s)

sequenceDiagram
    participant Host as Host Activity
    participant PM as PluginManager
    participant PRC as PluginResourceContext
    participant TS as IdeThemeService
    participant Plugin as Plugin UI

    Host->>PM: request plugin context
    PM->>PRC: create PluginResourceContext(plugin resources)
    PM->>TS: register IdeThemeServiceImpl(pluginContext)
    PRC->>TS: isDarkMode()
    TS-->>PRC: darkModeFlag
    alt darkModeFlag = true
        PRC->>PRC: resolve PluginTheme (night) and rebuild resources
    else
        PRC->>PRC: resolve PluginTheme (day) or fallback
    end
    PRC->>PRC: build/cache Theme & inflater
    PRC-->>PM: plugin context ready
    Host->>Plugin: inflate UI via PRC.inflateLayout(...)
    Plugin->>TS: addThemeChangeListener / isDarkMode() (optional)
    TS-->>Plugin: notify on theme changes
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • itsaky-adfa
  • jatezzz
  • jomen-adfa

Poem

🐰 Hopping through colors, soft and keen,
I paint PluginTheme where widgets convene.
Night and day I listen, swap, and hum,
Resources snug — the UI looks plum.
🌙🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (132 files):

⚔️ .github/workflows/debug.yml (content)
⚔️ .github/workflows/instrumentation-test.yml (content)
⚔️ .gitignore (content)
⚔️ apk-viewer-plugin/src/main/res/layout/fragment_sample.xml (content)
⚔️ app/build.gradle.kts (content)
⚔️ app/src/androidTest/kotlin/com/itsaky/androidide/app/strictmode/WhitelistRulesTest.kt (content)
⚔️ app/src/androidTest/kotlin/com/itsaky/androidide/scenarios/NavigateToMainScreenScenario.kt (content)
⚔️ app/src/main/googlePlay.png (content)
⚔️ app/src/main/ic_launcher-playstore.png (content)
⚔️ app/src/main/java/com/itsaky/androidide/actions/etc/DisconnectLogSendersAction.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/actions/etc/PreviewLayoutAction.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/actions/file/FormatCodeAction.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/activities/MainActivity.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/activities/OnboardingActivity.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/activities/editor/IDELogcatReader.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/agent/fragments/AiSettingsFragment.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/agent/fragments/ChatFragment.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/agent/repository/AgenticRunner.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/agent/repository/Executor.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/agent/repository/GeminiRepository.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/agent/repository/LlmInferenceEngine.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/agent/repository/LocalLlmRepositoryImpl.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/agent/repository/ModelFamily.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/agent/repository/SwitchableGeminiRepository.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/agent/repository/Tool.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/agent/repository/utils.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/agent/viewmodel/AiSettingsViewModel.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/agent/viewmodel/ChatViewModel.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/api/commands/ListFilesCommand.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/app/DeviceProtectedApplicationLoader.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/app/strictmode/ViolationHandler.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/app/strictmode/WhitelistEngine.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/assets/AssetsInstallationHelper.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/assets/BaseAssetsInstaller.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/assets/SplitAssetsInstaller.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/fragments/DiagnosticsListFragment.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/handlers/LspHandler.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/localWebServer/WebServer.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java (content)
⚔️ app/src/main/java/com/itsaky/androidide/services/debug/DebugOverlayManager.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/services/debug/DebuggerActionsOverlayAdapter.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/ui/themes/ThemeManager.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/utils/ApkInstaller.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/utils/DynamicLibraryLoader.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/viewmodel/BuildViewModel.kt (content)
⚔️ app/src/main/java/com/itsaky/androidide/viewmodel/InstallationViewModel.kt (content)
⚔️ app/src/main/res/layout/fragment_empty_state.xml (content)
⚔️ app/src/main/res/layout/layout_editor_bottom_sheet.xml (content)
⚔️ app/src/main/res/layout/layout_settings_local_llm.xml (content)
⚔️ app/src/main/res/values/strings.xml (content)
⚔️ build-info/build.gradle.kts (content)
⚔️ common/src/main/java/com/itsaky/androidide/app/BaseIDEActivity.kt (content)
⚔️ common/src/main/java/com/itsaky/androidide/syntax/colorschemes/DynamicColorScheme.kt (content)
⚔️ common/src/main/java/com/itsaky/androidide/syntax/colorschemes/SchemeAndroidIDE.java (content)
⚔️ common/src/main/java/com/itsaky/androidide/utils/Environment.java (content)
⚔️ common/src/main/java/com/itsaky/androidide/utils/FeedbackEmailHandler.kt (content)
⚔️ common/src/main/java/com/itsaky/androidide/utils/FeedbackManager.kt (content)
⚔️ common/src/main/java/com/itsaky/androidide/utils/OrientationUtilities.kt (content)
⚔️ composite-builds/build-logic/common/src/main/java/com/itsaky/androidide/build/config/BuildConfig.kt (content)
⚔️ composite-builds/build-logic/common/src/main/java/com/itsaky/androidide/build/config/CI.kt (content)
⚔️ composite-builds/build-logic/common/src/main/java/com/itsaky/androidide/build/config/ProjectConfig.kt (content)
⚔️ composite-builds/build-logic/plugins/build.gradle.kts (content)
⚔️ composite-builds/build-logic/plugins/src/main/java/com/itsaky/androidide/plugins/SigningConfigPlugin.kt (content)
⚔️ composite-builds/build-logic/plugins/src/main/java/com/itsaky/androidide/plugins/tasks/IDEWrapperGenerator.java (content)
⚔️ composite-builds/build-logic/plugins/src/main/java/com/itsaky/androidide/plugins/util/SigningKeyUtils.kt (content)
⚔️ editor/build.gradle.kts (content)
⚔️ editor/src/main/java/com/itsaky/androidide/editor/language/treesitter/KotlinLanguage.kt (content)
⚔️ editor/src/main/java/com/itsaky/androidide/editor/schemes/IDEColorScheme.kt (content)
⚔️ editor/src/main/java/com/itsaky/androidide/editor/ui/AbstractPopupWindow.kt (content)
⚔️ editor/src/main/java/com/itsaky/androidide/editor/ui/EditorActionsMenu.kt (content)
⚔️ editor/src/main/java/com/itsaky/androidide/editor/ui/EditorCompletionWindow.kt (content)
⚔️ editor/src/main/java/com/itsaky/androidide/editor/ui/EditorEventDispatcher.kt (content)
⚔️ editor/src/main/java/com/itsaky/androidide/editor/ui/IDEEditor.kt (content)
⚔️ flox/base/.flox/env/manifest.lock (content)
⚔️ flox/base/.flox/env/manifest.toml (content)
⚔️ flox/local/.flox/env/manifest.lock (content)
⚔️ gradle/libs.versions.toml (content)
⚔️ idetooltips/src/main/java/com/itsaky/androidide/idetooltips/ToolTipManager.kt (content)
⚔️ idetooltips/src/main/java/com/itsaky/androidide/idetooltips/TooltipTag.kt (content)
⚔️ images/icon.png (content)
⚔️ keystore-generator-plugin/src/main/AndroidManifest.xml (content)
⚔️ keystore-generator-plugin/src/main/kotlin/com/appdevforall/keygen/plugin/fragments/KeystoreGeneratorFragment.kt (content)
⚔️ keystore-generator-plugin/src/main/res/layout/fragment_keystore_generator.xml (content)
⚔️ layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/BaseActivity.kt (content)
⚔️ layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/activities/EditorActivity.kt (content)
⚔️ layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/activities/PreviewLayoutActivity.kt (content)
⚔️ layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/DesignEditor.kt (content)
⚔️ layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/tools/XmlLayoutGenerator.java (content)
⚔️ layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/tools/XmlLayoutParser.kt (content)
⚔️ llama-api/src/main/java/com/itsaky/androidide/llamacpp/api/ILlamaController.kt (content)
⚔️ llama-impl/src/main/cpp/llama-android.cpp (content)
⚔️ llama-impl/src/main/java/android/llama/cpp/LLamaAndroid.kt (content)
⚔️ logsender/src/main/java/com/itsaky/androidide/logsender/LogSenderService.java (content)
⚔️ logsender/src/main/res/values/strings.xml (content)
⚔️ plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/base/PluginFragment.kt (content)
⚔️ plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt (content)
⚔️ plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/documentation/PluginDocumentationManager.kt (content)
⚔️ plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/loaders/PluginResourceContext.kt (content)
⚔️ preferences/src/main/java/com/itsaky/androidide/preferences/internal/GeneralPreferences.kt (content)
⚔️ resources/src/main/ic_launcher-playstore.png (content)
⚔️ resources/src/main/res/drawable-xxxhdpi/template_basic_activity.png (content)
⚔️ resources/src/main/res/drawable-xxxhdpi/template_compose_empty_activity.png (content)
⚔️ resources/src/main/res/drawable-xxxhdpi/template_empty_noandroidx.png (content)
⚔️ resources/src/main/res/drawable-xxxhdpi/template_ndk_activity.png (content)
⚔️ resources/src/main/res/drawable-xxxhdpi/template_no_activity.png (content)
⚔️ resources/src/main/res/drawable-xxxhdpi/template_plugin.png (content)
⚔️ resources/src/main/res/drawable/app_dev_for_all_logo.png (content)
⚔️ resources/src/main/res/drawable/fat_pen.png (content)
⚔️ resources/src/main/res/values/strings.xml (content)
⚔️ resources/src/main/res/values/styles.xml (content)
⚔️ settings.gradle.kts (content)
⚔️ shared/src/main/java/com/itsaky/androidide/utils/DocumentUtils.java (content)
⚔️ subprojects/aapt2-proto/build.gradle.kts (content)
⚔️ subprojects/javac-services/src/main/java/com/itsaky/androidide/javac/services/fs/CachedJarFileSystem.kt (content)
⚔️ subprojects/project-models/build.gradle.kts (content)
⚔️ subprojects/projects/src/main/java/com/itsaky/androidide/projects/IProjectManager.kt (content)
⚔️ subprojects/projects/src/main/java/com/itsaky/androidide/projects/ProjectManagerImpl.kt (content)
⚔️ subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/AndroidModule.kt (content)
⚔️ subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/JavaModule.kt (content)
⚔️ subprojects/projects/src/main/java/com/itsaky/androidide/projects/api/ModuleProject.kt (content)
⚔️ subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/AbstractModelBuilder.kt (content)
⚔️ termux/termux-app/src/main/java/com/itsaky/androidide/activities/TerminalActivity.kt (content)
⚔️ termux/termux-app/src/main/java/com/termux/app/TermuxActivity.java (content)
⚔️ termux/termux-app/src/main/java/com/termux/app/terminal/TermuxTerminalSessionActivityClient.java (content)
⚔️ termux/termux-emulator/src/main/java/com/termux/terminal/TerminalSession.java (content)
⚔️ termux/termux-shared/src/main/java/com/termux/shared/termux/settings/properties/TermuxSharedProperties.java (content)
⚔️ testing/android/build.gradle.kts (content)

These conflicts must be resolved before merging into stage.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and specifically describes the main change: adding Material 3 theme support and IdeThemeService for plugins, which aligns with the comprehensive theme-related modifications across multiple files.
Description check ✅ Passed The description is directly related to the changeset, providing details about Material 3 theme support, IdeThemeService, PluginTheme additions, color resources, and bug fixes that match the actual changes made.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ADFA-1718
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch ADFA-1718
  • Create stacked PR with resolved conflicts
  • Post resolved changes as copyable diffs in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/loaders/PluginResourceContext.kt`:
- Around line 46-50: The theme lookup uses pluginPackageInfo?.packageName which
can be null; change the call that computes pluginThemeResId (the getIdentifier
invocation in PluginResourceContext) to pass the class's non-null packageName
(i.e., the overridden getPackageName()/packageName property on
PluginResourceContext) as the package parameter so getIdentifier reliably finds
"PluginTheme" and doesn't return 0 when pluginPackageInfo is null.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeThemeServiceImpl.kt`:
- Around line 12-29: Listeners are stored in a mutableList which can throw
ConcurrentModificationException if a listener removes itself during
notifyThemeChanged; replace the mutableList backing field with a thread-safe
CopyOnWriteArrayList and update addThemeChangeListener/removeThemeChangeListener
to operate on that list so notifyThemeChanged can safely call listeners.forEach
{ it.onThemeChanged(isDark) } without risking concurrent modification; reference
symbols: listeners, ThemeChangeListener, notifyThemeChanged,
addThemeChangeListener, removeThemeChangeListener.
🧹 Nitpick comments (1)
plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/base/PluginFragment.kt (1)

85-95: Avoid mutating shared Resources via updateConfiguration.
Resources.updateConfiguration is deprecated and mutates the shared pluginContext resources, which can leak configuration changes across fragments/activities. Prefer a configuration-scoped context and clone the inflater from it instead.

♻️ Suggested refactor
-        `@Suppress`("DEPRECATION")
-        pluginContext.resources.updateConfiguration(
-            activityContext.resources.configuration,
-            activityContext.resources.displayMetrics
-        )
-
-        val inflater = pluginContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as? LayoutInflater
-        return inflater ?: defaultInflater.cloneInContext(pluginContext)
+        val configContext =
+            pluginContext.createConfigurationContext(activityContext.resources.configuration)
+        val inflater =
+            configContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as? LayoutInflater
+        return inflater ?: defaultInflater.cloneInContext(configContext)

@Daniel-ADFA Daniel-ADFA requested a review from a team February 10, 2026 15:24
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/base/PluginFragment.kt`:
- Around line 88-89: The current inflation logic in PluginFragment obtains the
LayoutInflater via pluginContext.getSystemService which yields a plain
PhoneLayoutInflater and loses AppCompat/Material view factories; instead always
use the existing default inflater and clone it into pluginContext so
AppCompatViewInflater/Material factories are preserved. Modify the method that
returns the inflater in PluginFragment to remove the system-service path and
return defaultInflater.cloneInContext(pluginContext) unconditionally, ensuring
XML tags are correctly replaced by AppCompat/Material implementations.
🧹 Nitpick comments (5)
plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/base/PluginFragment.kt (1)

86-86: Bare pluginContext.theme access needs an explanatory comment.

This expression has no visible side effect. If the intent is to force lazy initialization of the Resources.Theme (so that the theme is resolved before the inflater is obtained), add a comment explaining that — otherwise it reads like dead code and will likely be removed in a future cleanup.

Suggested change
-        pluginContext.theme
+        // Force lazy initialization of the theme so that themed attributes
+        // are resolved correctly when the inflater is used.
+        pluginContext.theme
plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeThemeServiceImpl.kt (1)

26-26: Suppress or annotate the empty onLowMemory body.

Detekt flags this empty block. A brief inline comment (e.g., // no-op) or a @Suppress("EmptyFunctionBlock") annotation silences the warning while signalling intent.

plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt (2)

444-448: Consider adding dispose() to the IdeThemeService interface to avoid the downcast.

The downcast to IdeThemeServiceImpl couples PluginManager to the concrete type. If dispose() were part of the IdeThemeService contract (or a separate Disposable interface), you could eliminate the is check and keep the manager decoupled. Not blocking — current code is functionally correct and null-safe.


949-956: createPluginContext and createPluginContextWithResources are nearly identical — consider extracting shared service registration.

Both methods register the exact same set of services (project, UI, build, tooltip, editor_tab, file, sidebar, theme) with identical logic. The only difference is the Context passed to PluginContextImpl. Extracting a registerCommonServices(pluginServiceRegistry, pluginId, permissions) helper would eliminate duplication and ensure future service additions don't need to be made in two places.

plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/loaders/PluginResourceContext.kt (1)

144-147: Nit: redundant FQN when View is already imported.

android.view.ViewGroup and android.view.View are fully qualified in the signature, but View is already imported at Line 13. Consider importing ViewGroup as well for consistency.

Proposed fix

Add to imports:

import android.view.ViewGroup

Then simplify the signature:

-    fun inflateLayout(layoutResId: Int, root: android.view.ViewGroup? = null, attachToRoot: Boolean = false): android.view.View {
+    fun inflateLayout(layoutResId: Int, root: ViewGroup? = null, attachToRoot: Boolean = false): View {

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
keystore-generator-plugin/src/main/AndroidManifest.xml (1)

6-6: ⚠️ Potential issue | 🟡 Minor

Update manifest theme to PluginTheme for consistency.

The PR correctly introduces PluginTheme (Material 3 DayNight-based) in styles.xml. However, the manifest still declares @style/Theme.AppCompat, which is superseded at runtime. PluginResourceContext.getTheme() explicitly resolves PluginTheme via getIdentifier() and applies it, making the manifest-level declaration legacy/unused. Update line 6 to android:theme="@style/PluginTheme" to align manifest metadata with the actual runtime theme applied by PluginResourceContext.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
Verify each finding against the current code and only fix it if needed.


In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/loaders/PluginResourceContext.kt`:
- Around line 35-43: recreatePluginResources currently returns early when
pluginPackageInfo is null, leaving pluginResources stale and preventing
night-mode changes in getTheme; change the method so that if pluginPackageInfo
is null it logs a warning and rebuilds pluginResources from the host/base
context instead (use baseContext.resources.assets or baseContext.resources to
create a Resources instance with newConfig) so the fallback theme respects the
new configuration, and keep the existing asset-manager path logic when
pluginPackageInfo is present (refer to recreatePluginResources,
pluginPackageInfo, pluginResources, getTheme, and baseContext).
- Around line 156-158: The inflateLayout function uses fully-qualified types for
ViewGroup and View; add an import for android.view.ViewGroup and change the
signature of PluginResourceContext.inflateLayout (function name: inflateLayout)
to use the short type names (root: ViewGroup? = null, return type: View) to
match the existing imported View and keep the code consistent; update any
references in that file accordingly.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeThemeServiceImpl.kt`:
- Around line 11-13: IdeThemeServiceImpl currently stores the incoming Context
and registers ComponentCallbacks in its init block, which can leak an Activity
if one is passed; update the constructor/storage to use
context.applicationContext (store it in the class field used by the init block
and by dispose()) so the long-lived IdeThemeServiceImpl, its ComponentCallbacks
registration, and the dispose() lifecycle reference the application context
rather than a potential Activity context (identify places by the class name
IdeThemeServiceImpl, the constructor parameter context, the init block that
registers ComponentCallbacks, and the dispose() method).
- Line 27: The empty onLowMemory() in IdeThemeServiceImpl (from the
ComponentCallbacks interface) is flagged by detekt; either add a brief comment
explaining it's intentionally no-op (e.g., "// No-op: handled elsewhere" or
similar) or explicitly return Unit, or annotate the function with
`@Suppress`("EmptyFunctionBlock") to satisfy detekt; update the override of
onLowMemory() accordingly so the intent is explicit and the EmptyFunctionBlock
rule is addressed.
🧹 Nitpick comments (2)
🤖 Fix all nitpicks with AI agents
Verify each finding against the current code and only fix it if needed.


In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/loaders/PluginResourceContext.kt`:
- Around line 156-158: The inflateLayout function uses fully-qualified types for
ViewGroup and View; add an import for android.view.ViewGroup and change the
signature of PluginResourceContext.inflateLayout (function name: inflateLayout)
to use the short type names (root: ViewGroup? = null, return type: View) to
match the existing imported View and keep the code consistent; update any
references in that file accordingly.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeThemeServiceImpl.kt`:
- Line 27: The empty onLowMemory() in IdeThemeServiceImpl (from the
ComponentCallbacks interface) is flagged by detekt; either add a brief comment
explaining it's intentionally no-op (e.g., "// No-op: handled elsewhere" or
similar) or explicitly return Unit, or annotate the function with
`@Suppress`("EmptyFunctionBlock") to satisfy detekt; update the override of
onLowMemory() accordingly so the intent is explicit and the EmptyFunctionBlock
rule is addressed.
plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/loaders/PluginResourceContext.kt (1)

156-158: Use short names with an import for ViewGroup instead of FQNs.

View is already imported (line 13). Add an import for ViewGroup and use short names in the signature for consistency.

+import android.view.ViewGroup
 ...
-    fun inflateLayout(layoutResId: Int, root: android.view.ViewGroup? = null, attachToRoot: Boolean = false): android.view.View {
+    fun inflateLayout(layoutResId: Int, root: ViewGroup? = null, attachToRoot: Boolean = false): View {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/loaders/PluginResourceContext.kt`
around lines 156 - 158, The inflateLayout function uses fully-qualified types
for ViewGroup and View; add an import for android.view.ViewGroup and change the
signature of PluginResourceContext.inflateLayout (function name: inflateLayout)
to use the short type names (root: ViewGroup? = null, return type: View) to
match the existing imported View and keep the code consistent; update any
references in that file accordingly.
plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeThemeServiceImpl.kt (1)

27-27: Suppress or document the empty onLowMemory() to satisfy detekt.

This is required by the ComponentCallbacks interface but flagged by detekt's EmptyFunctionBlock rule. A brief comment or Unit return makes the intent explicit.

-        override fun onLowMemory() {}
+        override fun onLowMemory() { /* Required by ComponentCallbacks; nothing to release */ }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeThemeServiceImpl.kt`
at line 27, The empty onLowMemory() in IdeThemeServiceImpl (from the
ComponentCallbacks interface) is flagged by detekt; either add a brief comment
explaining it's intentionally no-op (e.g., "// No-op: handled elsewhere" or
similar) or explicitly return Unit, or annotate the function with
`@Suppress`("EmptyFunctionBlock") to satisfy detekt; update the override of
onLowMemory() accordingly so the intent is explicit and the EmptyFunctionBlock
rule is addressed.

@Daniel-ADFA Daniel-ADFA merged commit a004fc5 into stage Feb 17, 2026
2 checks passed
@Daniel-ADFA Daniel-ADFA deleted the ADFA-1718 branch February 17, 2026 20:18
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