Skip to content

ADFA-3169: Auto-generate unique plugin versions at build time#1104

Merged
Daniel-ADFA merged 4 commits into
stagefrom
ADFA-3169
Mar 24, 2026
Merged

ADFA-3169: Auto-generate unique plugin versions at build time#1104
Daniel-ADFA merged 4 commits into
stagefrom
ADFA-3169

Conversation

@Daniel-ADFA
Copy link
Copy Markdown
Contributor

No description provided.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 21, 2026

📝 Walkthrough

Release Notes: Auto-generate Unique Plugin Versions at Build Time

Features

  • Automatic plugin version generation per build variant: resolved as "-." (timestamp: yyyyMMddHHmmss) and injected into manifests via manifest placeholder ${pluginVersion}.
  • Optional manual override: pluginVersion: Property<String> added to PluginBuilderExtension to supply an explicit version.
  • Variant-aware builds: assemble task creation unified via a BuildVariant enum; output .cgp names are variant-specific (debug gets -debug suffix).
  • Automatic Android package-id assignment for AAPT parameters when not explicitly provided (adds --package-id and --allow-reserved-package-id).
  • Plugin template & generated manifests updated to emit ${pluginVersion} placeholder.
  • Plugin list UI truncates overly long version strings to show only the first three segments with ellipsis.
  • Plugin packaging/build plumbing: PluginBuilder wires into Android variant APIs and sets manifest placeholders per variant; resolved versions are logged.
  • Native library support for plugins:
    • APK native libs can be extracted into an app-private directory and wired into plugin classloader as the native lib dir.
    • New permission PluginPermission.NATIVE_CODE("native.code", "Execute native machine code") introduced to gate native code execution.
    • Plugin loading enforces the permission: extracted native libs are deleted and loading fails with a SecurityException if the plugin lacks NATIVE_CODE.
    • Extracted native libs are cleaned up on unload/uninstall/cache cleanup.
  • Resource/theme and inflation improvements:
    • PluginResourceContext now supports dynamic pluginClassLoader, accepts pluginId, chooses between plugin resources or current activity resources, and uses API-gated resource attachment (ResourcesLoader on API30+; reflection fallback).
    • PluginFragmentHelper snapshots activity Context/Theme and exposes getCurrentActivityTheme(pluginId) / getCurrentActivityContext(pluginId). Plugin registration now accepts isLegacy to alter inflater wiring.
    • Plugin loader/context creation updated to pass pluginId and nativeLibPath throughout.
  • PluginManager additions: native-lib extraction integration, activity-aware resource registration, getCurrentActivity() public accessor.
  • Keystore-generator plugin UI migration to Material Components (TextInputLayout/TextInputEditText, MaterialButton, LinearProgressIndicator), improved per-field validation and localized strings; theme/colors/styles updated to Material3 attributes and new color palette; many localized strings added.
  • Several manifests updated to use plugin-specific themes (e.g., PluginTheme, Theme.MarkdownPreviewer) and now use ${pluginVersion} placeholders.

Changes (selected)

  • Manifests: replaced hardcoded plugin.version values with ${pluginVersion} across plugin modules (apk-viewer, keystore-generator, markdown-preview, templates).
  • Build system: PluginBuilder adds variant manifest placeholder wiring, timestamp-based version resolution, package-id configuration, and variant-specific output naming.
  • Plugin loader/resource context: signatures changed to accept pluginId and native lib path; resource/theme resolution logic reworked and multiple fallback mechanisms introduced.
  • Plugin API: added NATIVE_CODE permission enum and PluginFragmentHelper API (new accessors and updated register signature).
  • UI/library: keystore plugin layout and fragment rewritten with Material components and improved error handling; resources (colors/styles/strings) updated.
  • Minor whitespace/packaging changes in several plugin build.gradle.kts files.

⚠️ Risks & Best-Practices Concerns

  • Non-deterministic builds: version includes LocalDateTime.now() timestamp → builds are not reproducible; identical source can produce different binaries.
  • Timezone variance: timestamp generation lacks explicit timezone handling; builds on different machines/timezones may produce different version strings.
  • Manifest placeholder fallback risk: if manifest placeholders are not injected correctly, manifests may end up containing the literal ${pluginVersion} string with no runtime validation or early failure.
  • Version validation: manually-set pluginVersion has no enforced format validation; malformed values could break tooling, UI parsing, or downstream consumers.
  • Package-id auto-assignment risk: automatically computing and appending --package-id may conflict with other resources or reserved IDs and could break resource merging on some projects if not opt-outable.
  • Resource attachment fragility: the multi-mode resource attachment (ResourcesLoader on R+, reflection fallback) may fail on some devices/ROMs; failures can cause resource/theme resolution errors at runtime.
  • Native-code security/attack surface:
    • Introducing NATIVE_CODE permission and loading native libs expands attack surface; mistakes in extraction, path handling, or permission checks could enable native code execution.
    • Extracted native libs are stored in app-private dirs but still increase risk; ensure strict validation and sandboxing expectations are documented.
  • Classloader/context changes: modifications to classloader/native-lib wiring and plugin context construction may introduce subtle classloading/resource mismatches and compatibility issues with existing plugins.
  • UI truncation ambiguity: version truncation in the plugin list reduces visible detail and may hinder debugging or precise version identification.
  • Backward-compatibility: changes to inflater behavior (legacy vs non-legacy paths) and new isLegacy flag may alter view inflation semantics for older plugins; test legacy plugins thoroughly.

Recommended review/testing focus

  • Reproducible-build implications and whether timestamping can be made opt-in or switchable to a stable build-id for CI.
  • Validation for pluginVersion property (format rules) and a safer failure mode if injection fails.
  • Conflict handling and opt-out for automatic --package-id assignment.
  • Thorough testing of resource attachment across API levels (especially API <30 and API 30+), and on varied OEM ROMs.
  • Security review for native extraction/loading path, permission enforcement, and cleanup logic.
  • Verify inflation/theme behavior for both legacy and non-legacy plugins, and ensure existing plugins remain functional.

Walkthrough

Replaces hardcoded plugin manifest versions with a ${pluginVersion} placeholder and implements build-time per-variant version resolution; adds native-library extraction/loading and a NATIVE_CODE permission, updates resource/theme wiring and inflater handling, and applies UI, style, color, and string changes for the keystore generator plugin.

Changes

Cohort / File(s) Summary
Build / Manifest placeholders
plugin-api/plugin-builder/src/main/.../PluginBuilder.kt, plugin-api/plugin-builder/src/main/.../PluginBuilderExtension.kt, templates-impl/src/main/.../pluginManifest.kt
Added pluginVersion extension property; variant-aware onVariants callback computes resolvedVersion (from config or app version + variant + timestamp) and injects pluginVersion manifest placeholder; template emits literal ${pluginVersion}.
Plugin Manifests
apk-viewer-plugin/src/main/AndroidManifest.xml, keystore-generator-plugin/src/main/AndroidManifest.xml, markdown-preview-plugin/src/main/AndroidManifest.xml
Replaced hardcoded plugin.version meta-data values with ${pluginVersion}; application theme references changed (to plugin-specific themes).
Plugin loading & native libs
plugin-manager/src/main/.../PluginManager.kt, plugin-manager/src/main/.../loaders/PluginLoader.kt, plugin-manager/src/main/.../loaders/PluginResourceContext.kt
Added native .so extraction, pass nativeLibPath into DexClassLoader, enforce NATIVE_CODE permission to allow native libs, cleanup extracted libs on unload/uninstall, make PluginResourceContext accept pluginId/classloader and switch resource/theme/inflater behavior based on package-id usage.
Plugin fragment / inflater helpers
plugin-api/src/main/.../PluginFragment.kt
Track per-plugin activity Context/Theme snapshots; expose accessors for current activity theme/context; registerPluginContext gains isLegacy flag and legacy-inflater path retained.
Permissions & templates
plugin-api/src/main/.../IPlugin.kt, templates-impl/src/main/.../PluginTemplateData.kt
Added PluginPermission.NATIVE_CODE enum constant and mirrored template permission entry.
Plugin list UI
app/src/main/java/com/itsaky/androidide/adapters/PluginListAdapter.kt
Display logic updated to truncate long dotted version strings to first three segments plus ....
Keystore plugin UI & resources
keystore-generator-plugin/src/main/.../KeystoreGeneratorFragment.kt, .../res/layout/fragment_keystore_generator.xml, .../res/values/*.xml
Migrated UI to Material components, per-field validation, lifecycle-aware coroutines, localized strings, added many strings, updated styles/colors (Material3 attributes) and night palette; layout and progress indicator refactor.
Misc build whitespace
*.gradle.kts (apk-viewer/keystore/markdown-preview plugins)
Minor whitespace/formatting changes only.

Sequence Diagram

sequenceDiagram
    participant Dev as Developer
    participant Gradle as Gradle/Build System
    participant PluginBuilder as PluginBuilder
    participant Manifest as AndroidManifest
    participant APK as APK Packager
    Dev->>Gradle: configure pluginVersion? / run build
    Gradle->>PluginBuilder: onVariants(variant)
    PluginBuilder->>PluginBuilder: resolveVersion (extension.value or baseVersion + variant + timestamp)
    PluginBuilder->>Manifest: set manifestPlaceholder "pluginVersion" = resolvedVersion
    PluginBuilder->>APK: package APK with resolved manifest values
Loading
sequenceDiagram
    participant App as Host App
    participant Loader as PluginLoader
    participant Manager as PluginManager
    participant Context as PluginResourceContext
    participant ClassLoader as DexClassLoader
    App->>Manager: loadPlugin(apk)
    Manager->>Loader: extractNativeLibs(pluginId)
    alt native libs found
        Loader->>Manager: nativeLibPath
        Manager->>Manager: check plugin.permissions for NATIVE_CODE
        alt permission present
            Manager->>Loader: loadPluginClasses(parentCL, nativeLibPath)
            Loader->>ClassLoader: create DexClassLoader(..., nativeLibPath)
            Loader->>Context: createPluginContext(pluginId)
            Manager->>App: register plugin (resources, classes)
        else permission missing
            Manager->>Loader: delete nativeLibPath
            Manager->>App: fail load with SecurityException
        end
    else no native libs
        Loader->>Manager: null
        Loader->>ClassLoader: create DexClassLoader(..., null)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • jomen-adfa
  • jatezzz
  • itsaky-adfa

Poem

🐰 I tunneled through manifests and build-time mist,
Replaced the old numbers with placeholders kissed,
Native hops guarded by a permission so bold,
Themes and colors refreshed, new stories told—
Hooray! A plugin parade, shiny, versioned, and swift. 🎉

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.39% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive No description was provided by the author, making it impossible to assess whether any description content is related to the changeset. Add a pull request description explaining the motivation, approach, and key changes for auto-generating unique plugin versions at build time.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the primary change: auto-generating unique plugin versions at build time, which is reflected across manifest updates, build logic changes, and version resolution implementation.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ADFA-3169

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
Copy Markdown
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.

🧹 Nitpick comments (2)
plugin-api/plugin-builder/src/main/kotlin/com/itsaky/androidide/plugins/build/PluginBuilder.kt (2)

13-16: Consider using UTC for timestamp consistency across build environments.

LocalDateTime.now() uses the system's default timezone, which could produce different version strings for the same commit when built on machines in different timezones. Consider using Instant.now() or LocalDateTime.now(ZoneOffset.UTC) for reproducibility.

♻️ Suggested fix for timezone consistency
 companion object {
-    private val TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss")
+    private val TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss")
+        .withZone(java.time.ZoneOffset.UTC)
     private const val DEFAULT_VERSION = "1.0.0"
 }

And update the usage:

-val timestamp = LocalDateTime.now().format(TIMESTAMP_FORMATTER)
+val timestamp = java.time.Instant.now().let { TIMESTAMP_FORMATTER.format(it) }

Also applies to: 33-33

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugin-api/plugin-builder/src/main/kotlin/com/itsaky/androidide/plugins/build/PluginBuilder.kt`
around lines 13 - 16, The timestamp uses the system default timezone (via
LocalDateTime.now()), causing non-reproducible version strings across machines;
update the timestamp generation to use UTC: either call
LocalDateTime.now(ZoneOffset.UTC) where the timestamp is created or switch to
Instant.now() and use a DateTimeFormatter withZone(ZoneOffset.UTC) so
TIMESTAMP_FORMATTER produces UTC timestamps consistently (update usages where
the timestamp is constructed, e.g., the code referencing TIMESTAMP_FORMATTER
around line 33).

53-54: Migrate project.buildDir to project.layout.buildDirectory for Gradle 8.3+ compatibility.

The buildDir property is deprecated as of Gradle 8.3 and will be removed in Gradle 10.0. Replace with layout.buildDirectory:

Example migration
// Before
val apkDir = File(project.buildDir, "outputs/apk/debug")

// After
val apkDir = project.layout.buildDirectory.dir("outputs/apk/debug").get().asFile

This applies to lines 53–54 (debug task) and 83–84 (release task).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugin-api/plugin-builder/src/main/kotlin/com/itsaky/androidide/plugins/build/PluginBuilder.kt`
around lines 53 - 54, Replace uses of the deprecated project.buildDir in
PluginBuilder.kt for both debug and release task sections (the apkDir and
outputDir assignments) with the new ProjectLayout API: use
project.layout.buildDirectory with dir(...) and then obtain the File via
get().asFile; update the apkDir and outputDir initializations in the debug block
(symbols apkDir and outputDir) and likewise in the release block to use
project.layout.buildDirectory.dir(...).get().asFile instead of
File(project.buildDir, ...).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@plugin-api/plugin-builder/src/main/kotlin/com/itsaky/androidide/plugins/build/PluginBuilder.kt`:
- Around line 13-16: The timestamp uses the system default timezone (via
LocalDateTime.now()), causing non-reproducible version strings across machines;
update the timestamp generation to use UTC: either call
LocalDateTime.now(ZoneOffset.UTC) where the timestamp is created or switch to
Instant.now() and use a DateTimeFormatter withZone(ZoneOffset.UTC) so
TIMESTAMP_FORMATTER produces UTC timestamps consistently (update usages where
the timestamp is constructed, e.g., the code referencing TIMESTAMP_FORMATTER
around line 33).
- Around line 53-54: Replace uses of the deprecated project.buildDir in
PluginBuilder.kt for both debug and release task sections (the apkDir and
outputDir assignments) with the new ProjectLayout API: use
project.layout.buildDirectory with dir(...) and then obtain the File via
get().asFile; update the apkDir and outputDir initializations in the debug block
(symbols apkDir and outputDir) and likewise in the release block to use
project.layout.buildDirectory.dir(...).get().asFile instead of
File(project.buildDir, ...).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2abdd090-5f9a-4b74-8650-bf777a478877

📥 Commits

Reviewing files that changed from the base of the PR and between 19e414c and 23e7daa.

📒 Files selected for processing (7)
  • apk-viewer-plugin/src/main/AndroidManifest.xml
  • app/src/main/java/com/itsaky/androidide/adapters/PluginListAdapter.kt
  • keystore-generator-plugin/src/main/AndroidManifest.xml
  • markdown-preview-plugin/src/main/AndroidManifest.xml
  • plugin-api/plugin-builder/src/main/kotlin/com/itsaky/androidide/plugins/build/PluginBuilder.kt
  • plugin-api/plugin-builder/src/main/kotlin/com/itsaky/androidide/plugins/build/PluginBuilderExtension.kt
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/pluginProject/pluginManifest.kt

@jatezzz jatezzz self-requested a review March 23, 2026 16:32
Copy link
Copy Markdown
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

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

86-119: Consider cleanup for cachedProvider and cachedLoader.

The cachedProvider and cachedLoader are stored but there's no explicit cleanup when the plugin is disposed. While this is typically long-lived, consider adding a dispose() or close() method to clean up these resources, especially cachedProvider which wraps file resources.

♻️ Optional: Add cleanup method
fun dispose() {
    cachedProvider?.close()
    cachedProvider = null
    cachedLoader = null
    recreatedAssetManager?.close()
    recreatedAssetManager = null
}
🤖 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 86 - 119, Add explicit cleanup for the ResourcesProvider and
ResourcesLoader used in ensurePluginResourcesAdded by adding a public
dispose/close method on PluginResourceContext that calls cachedProvider?.close()
(or appropriate close method), sets cachedProvider = null and cachedLoader =
null, and also closes/clears recreatedAssetManager if present; update any plugin
teardown path to call this new dispose() so file descriptors and loader
references are released when the plugin is disposed (reference symbols:
cachedProvider, cachedLoader, ensurePluginResourcesAdded, recreatedAssetManager,
dispose).
plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt (1)

316-332: Consider narrowing the exception handling for native lib extraction.

The current implementation catches a broad Exception (line 318), but based on project guidelines, prefer catching specific exception types. The extractNativeLibs method can throw IOException, SecurityException, or ZipException.

That said, the security enforcement logic is well-designed: deleting extracted libs and releasing sidebar slots before returning the failure ensures no partial state.

♻️ Optional: Narrow exception handling
         var nativeLibPath: String? = try {
             pluginLoader.extractNativeLibs(manifest.id)?.absolutePath
-        } catch (e: Exception) {
+        } catch (e: IOException) {
+            logger.warn("Failed to extract native libs for plugin: ${manifest.id}", e)
+            null
+        } catch (e: SecurityException) {
             logger.warn("Failed to extract native libs for plugin: ${manifest.id}", e)
             null
         }
🤖 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/core/PluginManager.kt`
around lines 316 - 332, Replace the broad catch(Exception) around
pluginLoader.extractNativeLibs(manifest.id) with specific catches for the known
exceptions (IOException, ZipException, and SecurityException), logging the same
warning and setting nativeLibPath to null in each handler; keep the surrounding
logic that checks nativeLibPath, deletes the extracted files with
File(nativeLibPath).deleteRecursively(), calls
SidebarSlotManager.releasePluginSlots(manifest.id) when manifest.sidebarItems >
0, and returns the same Result.failure(SecurityException(...)) if the plugin
lacks PluginPermission.NATIVE_CODE.
plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/loaders/PluginLoader.kt (1)

150-150: Consider iterating through supported ABIs if plugin fallback compatibility is needed.

The code uses Build.SUPPORTED_ABIS[0] which only attempts extraction for the primary ABI. If a plugin APK lacks libraries for the primary ABI but includes a compatible secondary ABI (e.g., primary is arm64-v8a but APK only has armeabi-v7a), extraction returns null. This appears intentional given the current design, and the null return is handled gracefully in PluginManager with appropriate error handling. However, if cross-ABI plugin compatibility becomes important, consider iterating through Build.SUPPORTED_ABIS to use the first available ABI in the APK.

🤖 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/PluginLoader.kt`
at line 150, Replace the hardcoded single-ABI prefix usage (val libPrefix =
"lib/${Build.SUPPORTED_ABIS[0]}/") with logic that iterates Build.SUPPORTED_ABIS
and attempts extraction using each ABI in order, returning the first successful
extraction; update the code that constructs libPrefix and the extraction routine
in PluginLoader (where libPrefix is used) so it tries each ABI (e.g.,
"lib/{abi}/") until a matching library is found or all ABIs fail.
keystore-generator-plugin/src/main/res/layout/fragment_keystore_generator.xml (1)

269-271: Consider using paddingBottom on the parent instead of a spacer View.

The bottom spacer could be replaced with android:paddingBottom="16dp" on the root LinearLayout. This slightly reduces the view hierarchy depth.

♻️ Suggested refactor
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:paddingBottom="16dp">

Then remove the spacer View at the end.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@keystore-generator-plugin/src/main/res/layout/fragment_keystore_generator.xml`
around lines 269 - 271, Replace the bottom spacer View with padding on the
parent LinearLayout: remove the <View> spacer (the final spacer with
android:layout_height="16dp") and add android:paddingBottom="16dp" to the root
LinearLayout element in fragment_keystore_generator.xml to reduce view hierarchy
depth and preserve the same spacing.
keystore-generator-plugin/src/main/kotlin/com/appdevforall/keygen/plugin/fragments/KeystoreGeneratorFragment.kt (2)

452-467: Consider using gradle.properties or environment variables for credentials.

The generated signing config writes passwords directly into the build.gradle file. While this matches common Android documentation patterns, it's a security concern if the build file is committed to version control.

A more secure approach would be to generate credentials in gradle.properties (which is typically gitignored) and reference them:

// In gradle.properties (gitignored)
KEYSTORE_PASSWORD=...

// In build.gradle
storePassword = project.findProperty('KEYSTORE_PASSWORD') ?: ''

This is a common security best practice but may be outside the scope of this PR.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@keystore-generator-plugin/src/main/kotlin/com/appdevforall/keygen/plugin/fragments/KeystoreGeneratorFragment.kt`
around lines 452 - 467, The current generation in KeystoreGeneratorFragment
builds a releaseConfig string that inlines storePassword and keyPassword (see
releaseConfig, isKotlinDsl and usages of
config.keystorePassword/config.keyPassword); change this to avoid embedding
credentials in build files by emitting property references instead and writing
the actual secrets into gradle.properties or using environment variables: update
the generator so it writes KEYSTORE_PASSWORD and KEY_PASSWORD entries (or reads
env vars) into the project gradle.properties (or instructs the user to do so)
and replace the inlined
"${String(config.keystorePassword)}"/'${String(config.keyPassword)}' with
project.findProperty(...) or System.getenv(...) references in releaseConfig for
both Kotlin and Groovy templates, ensuring no plaintext passwords remain in the
generated build.gradle fragment.

489-521: Regex pattern using [^}]* is technically fragile but works for standard signing configs.

The pattern will match until the first } character, which could break if signing configs contained nested braces or comments with }. However, no examples of such structures exist in the codebase—all signing configs follow the standard flat format used in Android development. If robustness is preferred, consider refactoring to a brace-matching approach or adding a comment documenting this assumption.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@keystore-generator-plugin/src/main/kotlin/com/appdevforall/keygen/plugin/fragments/KeystoreGeneratorFragment.kt`
around lines 489 - 521, The regexes createPattern, getByNamePattern and
groovyPattern use [^}]* which stops at the first '}' and can break with nested
braces; update KeystoreGeneratorFragment.kt to either (preferred short-term) add
a clear comment beside those patterns documenting the assumption that signing
configs are flat (no nested braces or '}' in comments) and why it's acceptable
here, or (if you want robustness now) replace the simplistic regex approach with
a proper brace-matching parser for the signing block (e.g., scan characters and
track brace depth for create("release"), getByName("release") and release { ...
} blocks) so nested braces or stray '}' inside strings/comments won't
prematurely terminate the match.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/base/PluginFragment.kt`:
- Around line 18-27: The mutable collections pluginContexts, serviceRegistries,
legacyPlugins and activitySnapshots are not thread-safe; replace them with
concurrent collections (e.g., use java.util.concurrent.ConcurrentHashMap-backed
maps for pluginContexts, serviceRegistries and activitySnapshots and a
concurrent key set for legacyPlugins) so accesses from IO/main threads won't
race; keep the ActivitySnapshot data class as-is and swap construction/usages of
these fields to the new concurrent maps/sets (refer to pluginContexts,
serviceRegistries, legacyPlugins, activitySnapshots and ActivitySnapshot to
locate all spots to update).

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/loaders/PluginLoader.kt`:
- Around line 143-179: The current extractNativeLibs(pluginId: String) returns
an existing pluginNativeDir without validating that its contents match the
pluginApk; change this by adding a freshness/completeness check: when
pluginNativeDir.exists() verify (a) a stored marker (e.g. checksum or
lastModified of pluginApk) inside the directory matches the current pluginApk
and/or (b) that the expected .so files listed from ZipFile(pluginApk) exist in
pluginNativeDir with matching sizes or checksums; if the marker mismatches or
files are missing/changed, deleteRecursively the pluginNativeDir and proceed to
re-extract as the existing code does, then update the marker and set
nativeLibDir before returning. Ensure you use the same symbols
(extractNativeLibs, pluginNativeDir, nativeLibDir, pluginApk) so the check
integrates cleanly with the existing flow.

---

Nitpick comments:
In
`@keystore-generator-plugin/src/main/kotlin/com/appdevforall/keygen/plugin/fragments/KeystoreGeneratorFragment.kt`:
- Around line 452-467: The current generation in KeystoreGeneratorFragment
builds a releaseConfig string that inlines storePassword and keyPassword (see
releaseConfig, isKotlinDsl and usages of
config.keystorePassword/config.keyPassword); change this to avoid embedding
credentials in build files by emitting property references instead and writing
the actual secrets into gradle.properties or using environment variables: update
the generator so it writes KEYSTORE_PASSWORD and KEY_PASSWORD entries (or reads
env vars) into the project gradle.properties (or instructs the user to do so)
and replace the inlined
"${String(config.keystorePassword)}"/'${String(config.keyPassword)}' with
project.findProperty(...) or System.getenv(...) references in releaseConfig for
both Kotlin and Groovy templates, ensuring no plaintext passwords remain in the
generated build.gradle fragment.
- Around line 489-521: The regexes createPattern, getByNamePattern and
groovyPattern use [^}]* which stops at the first '}' and can break with nested
braces; update KeystoreGeneratorFragment.kt to either (preferred short-term) add
a clear comment beside those patterns documenting the assumption that signing
configs are flat (no nested braces or '}' in comments) and why it's acceptable
here, or (if you want robustness now) replace the simplistic regex approach with
a proper brace-matching parser for the signing block (e.g., scan characters and
track brace depth for create("release"), getByName("release") and release { ...
} blocks) so nested braces or stray '}' inside strings/comments won't
prematurely terminate the match.

In
`@keystore-generator-plugin/src/main/res/layout/fragment_keystore_generator.xml`:
- Around line 269-271: Replace the bottom spacer View with padding on the parent
LinearLayout: remove the <View> spacer (the final spacer with
android:layout_height="16dp") and add android:paddingBottom="16dp" to the root
LinearLayout element in fragment_keystore_generator.xml to reduce view hierarchy
depth and preserve the same spacing.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt`:
- Around line 316-332: Replace the broad catch(Exception) around
pluginLoader.extractNativeLibs(manifest.id) with specific catches for the known
exceptions (IOException, ZipException, and SecurityException), logging the same
warning and setting nativeLibPath to null in each handler; keep the surrounding
logic that checks nativeLibPath, deletes the extracted files with
File(nativeLibPath).deleteRecursively(), calls
SidebarSlotManager.releasePluginSlots(manifest.id) when manifest.sidebarItems >
0, and returns the same Result.failure(SecurityException(...)) if the plugin
lacks PluginPermission.NATIVE_CODE.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/loaders/PluginLoader.kt`:
- Line 150: Replace the hardcoded single-ABI prefix usage (val libPrefix =
"lib/${Build.SUPPORTED_ABIS[0]}/") with logic that iterates Build.SUPPORTED_ABIS
and attempts extraction using each ABI in order, returning the first successful
extraction; update the code that constructs libPrefix and the extraction routine
in PluginLoader (where libPrefix is used) so it tries each ABI (e.g.,
"lib/{abi}/") until a matching library is found or all ABIs fail.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/loaders/PluginResourceContext.kt`:
- Around line 86-119: Add explicit cleanup for the ResourcesProvider and
ResourcesLoader used in ensurePluginResourcesAdded by adding a public
dispose/close method on PluginResourceContext that calls cachedProvider?.close()
(or appropriate close method), sets cachedProvider = null and cachedLoader =
null, and also closes/clears recreatedAssetManager if present; update any plugin
teardown path to call this new dispose() so file descriptors and loader
references are released when the plugin is disposed (reference symbols:
cachedProvider, cachedLoader, ensurePluginResourcesAdded, recreatedAssetManager,
dispose).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 26ccb27c-f043-436e-957f-ef84c27a02b7

📥 Commits

Reviewing files that changed from the base of the PR and between 23e7daa and 5a802fb.

📒 Files selected for processing (19)
  • apk-viewer-plugin/build.gradle.kts
  • apk-viewer-plugin/src/main/AndroidManifest.xml
  • keystore-generator-plugin/build.gradle.kts
  • keystore-generator-plugin/src/main/AndroidManifest.xml
  • keystore-generator-plugin/src/main/kotlin/com/appdevforall/keygen/plugin/fragments/KeystoreGeneratorFragment.kt
  • keystore-generator-plugin/src/main/res/layout/fragment_keystore_generator.xml
  • keystore-generator-plugin/src/main/res/values-night/colors.xml
  • keystore-generator-plugin/src/main/res/values/colors.xml
  • keystore-generator-plugin/src/main/res/values/strings.xml
  • keystore-generator-plugin/src/main/res/values/styles.xml
  • markdown-preview-plugin/build.gradle.kts
  • markdown-preview-plugin/src/main/AndroidManifest.xml
  • plugin-api/plugin-builder/src/main/kotlin/com/itsaky/androidide/plugins/build/PluginBuilder.kt
  • plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/IPlugin.kt
  • plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/base/PluginFragment.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/loaders/PluginLoader.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/loaders/PluginResourceContext.kt
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/pluginProject/PluginTemplateData.kt
✅ Files skipped from review due to trivial changes (7)
  • apk-viewer-plugin/build.gradle.kts
  • markdown-preview-plugin/build.gradle.kts
  • keystore-generator-plugin/build.gradle.kts
  • templates-impl/src/main/java/com/itsaky/androidide/templates/impl/pluginProject/PluginTemplateData.kt
  • keystore-generator-plugin/src/main/res/values-night/colors.xml
  • keystore-generator-plugin/src/main/res/values/strings.xml
  • keystore-generator-plugin/src/main/res/values/colors.xml
🚧 Files skipped from review as they are similar to previous changes (3)
  • apk-viewer-plugin/src/main/AndroidManifest.xml
  • keystore-generator-plugin/src/main/AndroidManifest.xml
  • plugin-api/plugin-builder/src/main/kotlin/com/itsaky/androidide/plugins/build/PluginBuilder.kt

# Conflicts:
#	apk-viewer-plugin/src/main/AndroidManifest.xml
#	keystore-generator-plugin/src/main/AndroidManifest.xml
#	markdown-preview-plugin/src/main/AndroidManifest.xml
#	plugin-api/plugin-builder/src/main/kotlin/com/itsaky/androidide/plugins/build/PluginBuilder.kt
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