ADFA-3162: Add FileOpenExtension plugin delegation, archive viewer, and Install action#1068
ADFA-3162: Add FileOpenExtension plugin delegation, archive viewer, and Install action#1068Daniel-ADFA merged 4 commits intostagefrom
Conversation
…nd Install action - Plugins can now intercept file opens via FileOpenExtension (canHandleFileOpen/handleFileOpen) - APK files delegate directly to APK Analyzer plugin when installed (single-click analysis) - APK/CGP/ZIP files open as read-only archive listings when no plugin handles them - Add Install tab action for APK (device install) and CGP (plugin install with feedback) - Plugin file menu items appear in both tab popup and file tree context menu
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds a FileOpenExtension API and integrates plugin-driven file-open delegation, file-tab menu items, and lifecycle notifications. APK viewer implements the extension with deferred analysis; editor UI surfaces plugin menu items; archives (apk/cgp/zip) are shown as read-only listings; an Install action and plugin metadata handling were added/updated. Changes
Sequence DiagramsequenceDiagram
participant User
participant Editor as EditorHandlerActivity
participant PM as PluginManager
participant Plugin as FileOpenExtension\n(e.g., ApkViewer)
participant Fragment as ApkAnalyzerFragment
User->>Editor: Open file (file.apk)
Editor->>PM: delegateFileOpen(file)
PM->>Plugin: canHandleFileOpen(file)?
Plugin-->>PM: true
PM->>Plugin: handleFileOpen(file)
Plugin->>Fragment: analyzeFile(file) / queue pendingAnalysisFile
PM-->>Editor: handled (skip standard open)
User->>Editor: Open file tab menu
Editor->>PM: getFileTabMenuItems(file)
PM->>Plugin: getFileTabMenuItems(file)
Plugin-->>PM: [FileTabMenuItem]
PM-->>Editor: aggregated menu items
Editor->>Editor: showPopupWindow(..., pluginMenuItems)
User->>Editor: Close file
Editor->>PM: notifyFileClosed(file)
PM->>Plugin: onFileClosed(file)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
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 Tip You can make CodeRabbit's review stricter and more nitpicky using the `assertive` profile, if that's what you prefer.Change the |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (4)
app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt (2)
334-335: Consider adding a log statement for debugging purposes.The silent
return falseis functionally correct since archive files are made non-editable ininitializeArchiveContent(). However, adding a debug log here would help trace save operations during troubleshooting:- if (file.extension.lowercase() in ARCHIVE_EXTENSIONS) return false + if (file.extension.lowercase() in ARCHIVE_EXTENSIONS) { + log.debug("Skipping save for archive file: {}", file.name) + return false + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt` around lines 334 - 335, Add a short debug log before the early-return that rejects archive files: in CodeEditorView where the check "if (file.extension.lowercase() in ARCHIVE_EXTENSIONS) return false" occurs, log the file name and extension (using the existing logger or android.util.Log/Timber) so save attempts on archive files are visible during debugging; keep the return behavior unchanged and reference initializeArchiveContent() for context.
509-513: Consider narrowing the exception catch.While catching
Exceptionprovides a safe fallback for user display, it could mask unexpected errors during development. Consider catching the specific exceptions thatZipFilecan throw:- } catch (e: Exception) { + } catch (e: java.util.zip.ZipException) { + builder.clear() + builder.appendLine("Failed to read archive: ${file.name}") + builder.appendLine(e.message ?: "Invalid or corrupt archive") + } catch (e: java.io.IOException) { builder.clear() builder.appendLine("Failed to read archive: ${file.name}") builder.appendLine(e.message ?: "Unknown error") }Based on learnings: "prefer narrow exception handling that catches only the specific exception type... This aligns with fail-fast behavior during development."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt` around lines 509 - 513, The current broad catch (e: Exception) in CodeEditorView should be narrowed to only handle the expected archive-related errors: catch ZipException and IOException (or the concrete exceptions thrown by ZipFile) and keep the existing builder.clear()/appendLine(...) handling in those catches; remove the general Exception catch so unexpected runtime errors can propagate (fail fast) during development. Ensure you refer to the existing symbols (builder, file.name) when implementing the two specific catch blocks and preserve the user-facing messages there.plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/extensions/FileOpenExtension.kt (1)
19-27: Consider documenting theiconandactionfields.The
FileTabMenuItemdata class is functional, but consumers would benefit from brief documentation clarifying thaticonexpects an Android drawable resource ID and thatactionexceptions should be handled by callers.📝 Suggested documentation
+/** + * Represents a menu item contributed by a plugin for file tab context menus. + * `@param` id Unique identifier for this menu item + * `@param` title Display text for the menu item + * `@param` icon Optional drawable resource ID for the icon + * `@param` isEnabled Whether the item is enabled + * `@param` isVisible Whether the item is visible + * `@param` order Sort order (lower values appear first) + * `@param` action Callback invoked when the item is clicked; callers must handle exceptions + */ data class FileTabMenuItem( val id: String, val title: String, val icon: Int? = null, val isEnabled: Boolean = true, val isVisible: Boolean = true, val order: Int = 0, val action: () -> Unit )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/extensions/FileOpenExtension.kt` around lines 19 - 27, Add KDoc to the FileTabMenuItem data class clarifying the semantics of the icon and action properties: document that icon is an Android drawable resource ID (Int) and may be null when no icon is needed, and document that action is a callback invoked on selection and that callers are responsible for handling or catching any exceptions thrown by the action; place the comments on the FileTabMenuItem class and/or directly above the icon and action parameters to make the contract clear to consumers.plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt (1)
604-613: Consider fallback behavior when the selected handler fails.If
canHandleFileOpenreturnstruebuthandleFileOpenthrows or returnsfalse, the delegation returnsfalsewithout trying other capable extensions. This is acceptable if the "first match wins" semantic is intentional, but consider whether a fallback to subsequent handlers would be more resilient.♻️ Optional: Try next handler on failure
fun delegateFileOpen(file: File): Boolean { - val handler = getEnabledFileOpenExtensions().firstOrNull { extension -> - executeWithErrorHandling("check canHandleFileOpen") { - extension.canHandleFileOpen(file) - }.getOrDefault(false) - } ?: return false - return executeWithErrorHandling("handle file open") { - handler.handleFileOpen(file) - }.getOrDefault(false) + for (extension in getEnabledFileOpenExtensions()) { + val canHandle = executeWithErrorHandling("check canHandleFileOpen") { + extension.canHandleFileOpen(file) + }.getOrDefault(false) + if (canHandle) { + val handled = executeWithErrorHandling("handle file open") { + extension.handleFileOpen(file) + }.getOrDefault(false) + if (handled) return true + } + } + return false }🤖 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 604 - 613, delegateFileOpen currently picks the first extension where getEnabledFileOpenExtensions().firstOrNull { ... canHandleFileOpen(...) } and if that handler's handleFileOpen(...) fails or returns false the method returns false without trying other capable extensions; change it to iterate through the enabled extensions, call executeWithErrorHandling("check canHandleFileOpen") { extension.canHandleFileOpen(file) } and for each true candidate call executeWithErrorHandling("handle file open") { extension.handleFileOpen(file) }, returning true immediately on the first successful handleFileOpen(true) and otherwise continuing to the next candidate (and finally returning false if none succeed); preserve existing error handling/logging via executeWithErrorHandling and ensure exceptions from one handler do not prevent trying subsequent handlers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apk-viewer-plugin/src/main/AndroidManifest.xml`:
- Around line 17-19: The manifest still declares plugin.min_ide_version as 1.0.0
while the plugin now unconditionally implements FileOpenExtension; update the
manifest entry plugin.min_ide_version to "2.1.0" to match the IDE release that
introduced FileOpenExtension so older IDEs won't load the incompatible plugin;
locate the meta-data element named plugin.min_ide_version in the
AndroidManifest.xml (alongside plugin.version) and change its android:value to
"2.1.0".
In `@apk-viewer-plugin/src/main/kotlin/com/example/sampleplugin/ApkViewer.kt`:
- Around line 129-136: The pendingAnalysisFile can be left unprocessed if the
APK Analyzer tab is already selected because onEditorTabSelected only fires on
selection changes; update openApkAnalyzerTab (after calling
selectPluginTab(TAB_ID)) to query the current tab/fragment from the
IdeEditorTabService (or equivalent) and if the current tab id equals TAB_ID and
the fragment is an ApkAnalyzerFragment, call
fragment.analyzeFile(pendingAnalysisFile) and clear pendingAnalysisFile (same
behavior as in onEditorTabSelected), with null and type checks; this ensures
pendingAnalysisFile is handled whether the tab is newly selected or already
active.
In
`@apk-viewer-plugin/src/main/kotlin/com/example/sampleplugin/fragments/ApkAnalyzerFragment.kt`:
- Around line 149-158: The ZipFile opened in analyzeApkFromFile (variable
zipFile) can leak if an exception is thrown; replace the manual ZipFile creation
with Kotlin's use{} (or ensure a try/finally that closes zipFile) so the ZipFile
is closed automatically after processing; update the block that builds
entries/entryMap and any further access to the zip to occur inside the use{}
scope to guarantee proper resource cleanup.
In `@app/src/main/java/com/itsaky/androidide/actions/file/InstallFileAction.kt`:
- Around line 50-68: The doAction implementation in EditorHandlerActivity
obtains a file via editorViewModel.getCurrentFile() and switches on
file.extension.lowercase(), but lacks an else/default branch so files with no or
unhandled extensions result in no action while the method still returns true;
modify EditorHandlerActivity.doAction to treat unrecognized/empty extensions as
no-op failures by adding an else branch (or explicit handling for empty
file.extension) that returns false (or otherwise signals no action taken) so the
method only returns true when an action (e.g.,
apkInstallationViewModel.installApk or repo.installPluginFromFile) was actually
invoked.
In `@app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt`:
- Around line 479-498: The loop that builds the ZIP listing currently uses
entry.size, entry.compressedSize, and entry.crc directly which can be -1 for
unknown values; change the logic in the entries iteration (the block that
updates totalSize/totalCompressed and calls builder.appendLine with
String.format) to: treat any entry.size or entry.compressedSize == -1 as
"unknown" (display "-" in the formatted columns and do NOT add them to
totalSize/totalCompressed), and treat entry.crc == -1 as "-" (instead of
formatting to "ffffffff"); adjust the values passed to String.format accordingly
so totals remain non-negative and unknowns are shown as "-" while keeping the
existing method/time/name formatting.
- Around line 448-460: The archive-loading path in initializeArchiveContent does
not refresh the Activity options menu like initializeContent does; after calling
configureEditorIfNeeded() in initializeArchiveContent(), add a menu invalidation
call: invoke (context as? Activity?)?.invalidateOptionsMenu() so the UI reflects
the new read-only file state; update initializeArchiveContent to call this
(mirroring the postRead behavior referenced in initializeContent) to keep menu
items synchronized.
---
Nitpick comments:
In `@app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt`:
- Around line 334-335: Add a short debug log before the early-return that
rejects archive files: in CodeEditorView where the check "if
(file.extension.lowercase() in ARCHIVE_EXTENSIONS) return false" occurs, log the
file name and extension (using the existing logger or android.util.Log/Timber)
so save attempts on archive files are visible during debugging; keep the return
behavior unchanged and reference initializeArchiveContent() for context.
- Around line 509-513: The current broad catch (e: Exception) in CodeEditorView
should be narrowed to only handle the expected archive-related errors: catch
ZipException and IOException (or the concrete exceptions thrown by ZipFile) and
keep the existing builder.clear()/appendLine(...) handling in those catches;
remove the general Exception catch so unexpected runtime errors can propagate
(fail fast) during development. Ensure you refer to the existing symbols
(builder, file.name) when implementing the two specific catch blocks and
preserve the user-facing messages there.
In
`@plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/extensions/FileOpenExtension.kt`:
- Around line 19-27: Add KDoc to the FileTabMenuItem data class clarifying the
semantics of the icon and action properties: document that icon is an Android
drawable resource ID (Int) and may be null when no icon is needed, and document
that action is a callback invoked on selection and that callers are responsible
for handling or catching any exceptions thrown by the action; place the comments
on the FileTabMenuItem class and/or directly above the icon and action
parameters to make the contract clear to consumers.
In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt`:
- Around line 604-613: delegateFileOpen currently picks the first extension
where getEnabledFileOpenExtensions().firstOrNull { ... canHandleFileOpen(...) }
and if that handler's handleFileOpen(...) fails or returns false the method
returns false without trying other capable extensions; change it to iterate
through the enabled extensions, call executeWithErrorHandling("check
canHandleFileOpen") { extension.canHandleFileOpen(file) } and for each true
candidate call executeWithErrorHandling("handle file open") {
extension.handleFileOpen(file) }, returning true immediately on the first
successful handleFileOpen(true) and otherwise continuing to the next candidate
(and finally returning false if none succeed); preserve existing error
handling/logging via executeWithErrorHandling and ensure exceptions from one
handler do not prevent trying subsequent handlers.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: bf553b7e-e9bf-499c-b830-9f412e46eb43
📒 Files selected for processing (13)
apk-viewer-plugin/src/main/AndroidManifest.xmlapk-viewer-plugin/src/main/kotlin/com/example/sampleplugin/ApkViewer.ktapk-viewer-plugin/src/main/kotlin/com/example/sampleplugin/fragments/ApkAnalyzerFragment.ktapp/src/main/java/com/itsaky/androidide/actions/file/InstallFileAction.ktapp/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.ktapp/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.ktapp/src/main/java/com/itsaky/androidide/handlers/FileTreeActionHandler.ktapp/src/main/java/com/itsaky/androidide/ui/CodeEditorView.ktapp/src/main/java/com/itsaky/androidide/utils/ActionMenuUtils.ktapp/src/main/java/com/itsaky/androidide/utils/EditorActivityActions.ktplugin-api/src/main/kotlin/com/itsaky/androidide/plugins/extensions/FileOpenExtension.ktplugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.ktresources/src/main/res/values/strings.xml
apk-viewer-plugin/src/main/kotlin/com/example/sampleplugin/fragments/ApkAnalyzerFragment.kt
Show resolved
Hide resolved
app/src/main/java/com/itsaky/androidide/actions/file/InstallFileAction.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt (3)
475-476: Use explicitLocale.ROOTfor consistent formatting.
String.formatwithout a locale uses the device's default locale, which can produce inconsistent output (e.g., decimal separators varying between,and.for the compression ratio). Since this is a technical listing, useLocale.ROOTfor consistent formatting.♻️ Proposed fix
+import java.util.LocaleThen in
generateArchiveListing:builder.appendLine( - String.format("%-10s %-10s %-6s %-8s %-20s %s", + String.format(Locale.ROOT, "%-10s %-10s %-6s %-8s %-20s %s", "Length", "Compressed", "Method", "CRC-32", "Date & Time", "Name") )- val crc = if (entry.crc >= 0) String.format("%08x", entry.crc) else "-" + val crc = if (entry.crc >= 0) String.format(Locale.ROOT, "%08x", entry.crc) else "-"builder.appendLine( - String.format("%-10s %-10s %-6s %-8s %-20s %s", + String.format(Locale.ROOT, "%-10s %-10s %-6s %-8s %-20s %s", sizeStr, compressedStr, method, crc, time, entry.name) )builder.appendLine( - String.format("%-10d %-10d %-6s %s", + String.format(Locale.ROOT, "%-10d %-10d %-6s %s", totalSize, totalCompressed, ratio, "${entries.size} files") )Also applies to: 489-489, 500-501, 510-511
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt` around lines 475 - 476, The String.format calls in generateArchiveListing (including the header at the shown line and the other occurrences around lines 489, 500-501, 510-511) should be made locale-independent by passing Locale.ROOT as the first argument; update each String.format(...) call used for the archive listing (e.g., the header format and the per-entry formatting in generateArchiveListing) to call String.format(Locale.ROOT, formatString, ...), ensuring consistent numeric/decimal formatting across locales.
514-518: Prefer narrow exception handling forZipFileoperations.Catching the broad
Exceptiontype can mask unexpected errors.ZipFileoperations typically throwIOExceptionorZipException(which extendsIOException), and file access may throwSecurityException.♻️ Proposed fix
- } catch (e: Exception) { + } catch (e: IOException) { builder.clear() builder.appendLine("Failed to read archive: ${file.name}") builder.appendLine(e.message ?: "Unknown error") + } catch (e: SecurityException) { + builder.clear() + builder.appendLine("Failed to read archive: ${file.name}") + builder.appendLine("Permission denied: ${e.message ?: "Unknown error"}") }Based on learnings: "In Kotlin files across the AndroidIDE project, prefer narrow exception handling that catches only the specific exception type... instead of a broad catch-all."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt` around lines 514 - 518, The current catch-all "catch (e: Exception)" in CodeEditorView.kt should be narrowed to handle the specific ZipFile-related errors: replace it with separate catches for java.util.zip.ZipException (or java.util.zip.ZipException/IOException combined), java.io.IOException, and java.lang.SecurityException in the method/block that reads archives (the one that uses builder and file.name), and in each handler keep the builder.clear() and append descriptive messages including the exception details (e.g., e.message or e.localizedMessage) so callers still see "Failed to read archive: ${file.name}" plus the specific error; remove the generic Exception catch to avoid masking other issues.
334-335: Consider adding a log statement for clarity.While this follows the existing pattern (unmodified files also return
false), a log message would help distinguish "archive files cannot be saved" from "file wasn't modified" when debugging save operations.📝 Proposed improvement
- if (file.extension.lowercase() in ARCHIVE_EXTENSIONS) return false + if (file.extension.lowercase() in ARCHIVE_EXTENSIONS) { + log.debug("Skipping save operation for archive file {}", file.name) + return false + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt` around lines 334 - 335, In CodeEditorView, before the early return that checks "if (file.extension.lowercase() in ARCHIVE_EXTENSIONS) return false", add a log statement that clearly records that the file is an archive and therefore not savable (include file.name and file.extension in the message); use the project's logging mechanism (e.g., Android Log or the existing logger used elsewhere in CodeEditorView) so this case is distinguishable from the "not modified" return path.apk-viewer-plugin/src/main/kotlin/com/example/sampleplugin/fragments/ApkAnalyzerFragment.kt (1)
152-153: Prefer explicit ZIP/IO catches here.
runCatchingcatchesThrowable, so fatal VM errors also get converted into a normal"Failed to analyze APK"result. It would be safer to catch the expected parsing failures explicitly instead of wrapping the whole method in a catch-all.Based on learnings: In Kotlin files across the AndroidIDE project, prefer narrow exception handling that catches only the specific exception type reported in crashes instead of a broad catch-all.
Also applies to: 335-337
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apk-viewer-plugin/src/main/kotlin/com/example/sampleplugin/fragments/ApkAnalyzerFragment.kt` around lines 152 - 153, The use of runCatching around the ZipFile(file).use { ... } in ApkAnalyzerFragment wraps all Throwables (including fatal VM errors); replace the broad runCatching with a try { ZipFile(file).use { ... } } catch (e: java.util.zip.ZipException) { /* handle/log specific zip parse error and return failure result */ } catch (e: java.io.IOException) { /* handle/log IO error and return failure result */ } so only expected parsing/IO failures are caught (do not catch Throwable or Exception); update the same pattern where runCatching is used again in the file (the other similar block) to mirror this explicit ZipException/IOException handling and return the same failure result.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apk-viewer-plugin/src/main/kotlin/com/example/sampleplugin/fragments/ApkAnalyzerFragment.kt`:
- Around line 115-117: analyzeApkInBackground currently calls runAnalysis
without the same lifecycle guard used in analyzeFile, which can access
viewLifecycleOwner after onDestroyView and will also hide CancellationException
inside runCatching; fix by checking the fragment view lifecycle before starting
work (e.g., if (!isAdded || view == null) return) in analyzeApkInBackground (or
at the top of runAnalysis), and update the error handling around
withContext/runCatching so CancellationException is rethrown instead of being
converted to a failure message (catch Throwable but if it is a
CancellationException rethrow it, otherwise handle/log the error). Ensure
references: analyzeApkInBackground, analyzeFile, runAnalysis, runCatching,
withContext, viewLifecycleOwner.
- Around line 137-145: The analyzeApkFromUri function currently ignores a null
InputStream from requireContext().contentResolver.openInputStream(uri) and
proceeds to call analyzeApkFromFile on an empty tempFile; change
analyzeApkFromUri to explicitly handle a null return: after creating tempFile,
call openInputStream(uri) into a local val (e.g. val input =
requireContext().contentResolver.openInputStream(uri)), if input is null delete
tempFile and throw or return a clear error indicating the URI could not be
opened (or return a meaningful Result), otherwise use input.use { ... } to copy
to tempFile and then call analyzeApkFromFile(tempFile); ensure tempFile is still
deleted in the finally block.
---
Nitpick comments:
In
`@apk-viewer-plugin/src/main/kotlin/com/example/sampleplugin/fragments/ApkAnalyzerFragment.kt`:
- Around line 152-153: The use of runCatching around the ZipFile(file).use { ...
} in ApkAnalyzerFragment wraps all Throwables (including fatal VM errors);
replace the broad runCatching with a try { ZipFile(file).use { ... } } catch (e:
java.util.zip.ZipException) { /* handle/log specific zip parse error and return
failure result */ } catch (e: java.io.IOException) { /* handle/log IO error and
return failure result */ } so only expected parsing/IO failures are caught (do
not catch Throwable or Exception); update the same pattern where runCatching is
used again in the file (the other similar block) to mirror this explicit
ZipException/IOException handling and return the same failure result.
In `@app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt`:
- Around line 475-476: The String.format calls in generateArchiveListing
(including the header at the shown line and the other occurrences around lines
489, 500-501, 510-511) should be made locale-independent by passing Locale.ROOT
as the first argument; update each String.format(...) call used for the archive
listing (e.g., the header format and the per-entry formatting in
generateArchiveListing) to call String.format(Locale.ROOT, formatString, ...),
ensuring consistent numeric/decimal formatting across locales.
- Around line 514-518: The current catch-all "catch (e: Exception)" in
CodeEditorView.kt should be narrowed to handle the specific ZipFile-related
errors: replace it with separate catches for java.util.zip.ZipException (or
java.util.zip.ZipException/IOException combined), java.io.IOException, and
java.lang.SecurityException in the method/block that reads archives (the one
that uses builder and file.name), and in each handler keep the builder.clear()
and append descriptive messages including the exception details (e.g., e.message
or e.localizedMessage) so callers still see "Failed to read archive:
${file.name}" plus the specific error; remove the generic Exception catch to
avoid masking other issues.
- Around line 334-335: In CodeEditorView, before the early return that checks
"if (file.extension.lowercase() in ARCHIVE_EXTENSIONS) return false", add a log
statement that clearly records that the file is an archive and therefore not
savable (include file.name and file.extension in the message); use the project's
logging mechanism (e.g., Android Log or the existing logger used elsewhere in
CodeEditorView) so this case is distinguishable from the "not modified" return
path.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e2f4437e-b4e7-4e6b-bc8e-71715d2796cb
📒 Files selected for processing (2)
apk-viewer-plugin/src/main/kotlin/com/example/sampleplugin/fragments/ApkAnalyzerFragment.ktapp/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt
FileOpenExtensionplugin apiScreen.Recording.2026-03-12.at.11.01.56.mov