Skip to content

ADFA-4649: Remove com.blankj:utilcodex (AndroidUtilCode) dependency - #1576

Merged
davidschachterADFA merged 24 commits into
stagefrom
fix/ADFA-4649-remove-blankj-utilcode
Jul 29, 2026
Merged

ADFA-4649: Remove com.blankj:utilcodex (AndroidUtilCode) dependency#1576
davidschachterADFA merged 24 commits into
stagefrom
fix/ADFA-4649-remove-blankj-utilcode

Conversation

@davidschachterADFA

@davidschachterADFA davidschachterADFA commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Removes the com.blankj:utilcodex ("AndroidUtilCode") dependency entirely — it duplicated functionality AndroidX core, Kotlin stdlib, and coroutines already provide, per the ticket.
  • Real usage was much wider than the ticket's "revisit later, <1MB" note suggested: 71 files across 11 modules, spanning 20 different utility classes (SizeUtils, ThreadUtils, FileUtils/FileIOUtils, ClipboardUtils/KeyboardUtils/NetworkUtils, DeviceUtils/AppUtils/ConvertUtils/ThrowableUtils/StringUtils/CloseUtils/ArrayUtils/Utils/ActivityUtils, ReflectUtils/ImageUtils/ResourceUtils/ZipUtils).
  • Staged as 7 commits (easiest → hardest), each independently reviewable:
    1. SizeUtils/ConvertUtils dp↔px conversions → Context.dpToPx()/spToPx() extensions
    2. ThreadUtils → a shared mainThreadHandler + native runOnUiThread()
    3. FileUtils/FileIOUtils → same-name native drop-ins (isUtf8, readFile2String, writeFileFromString, listFilesInDirWithFilter, delete, rename, getFileExtension, createOrExistsDir) so most call sites only needed an import swap
    4. ClipboardUtils/KeyboardUtils/NetworkUtilsContext extensions (copyToClipboard, isNetworkConnected, isSoftInputVisible via WindowInsetsCompat's IME check instead of blankj's decor-view-height heuristic)
    5. The remaining misc utilities (DeviceUtils, AppUtils, ThrowableUtils.getFullStackTrace → Kotlin's stackTraceToString(), CloseUtils, ArrayUtils, StringUtils, Utils.getApp()/ActivityUtilsBaseApplication)
    6. The hardest tier: a same-name ReflectUtils port (verified bug-for-bug against the actual blankj bytecode via javap, since a couple of call sites in UnEnter.kt chain in a way that only works if the port matches blankj's exact field-wrapping semantics), plus ImageUtils, ResourceUtils (recursive asset copy), ZipUtils (with zip-slip protection added as a safe hardening)
    7. Drop the dependency declaration from all 12 modules that had it + libs.versions.toml

Verification

  • Zero com.blankj.utilcode source references remain anywhere in the repo
  • :app:compileV8DebugKotlin/compileV8DebugJavaWithJavac build clean after each stage
  • Full unit test suite passes post-removal: 190 tests, 0 failures (app/common/editor/xml-inflater/lsp-java)
  • :app:assembleV8Debug succeeds; resulting APK contains zero blankj/utilcode classes
  • Measured actual APK size impact by building before/after: debug APK shrinks ~452KB (dex bytecode -560KB uncompressed)
  • spotlessCheck passes (pre-push hook)

Test plan

  • CI build/tests green
  • Smoke-test on-device: flashbar messages (success/error/info toasts), clipboard copy (About screen, file-tree "copy path"), soft-keyboard-aware UI (editor bottom sheet, fullscreen toggle), Git clone/push/pull network-connectivity gating, file-tree operations (delete/rename/create), template project creation (asset copying), Gradle wrapper install (zip extraction), Java LSP code actions that use reflection (generate constructor/toString/getters-setters, override methods), XML inflater's ToggleButton/GestureOverlayView attribute adapters, crash reporting (GlitchTip tags: app_version_code, device_emulator, device_rooted)

🤖 Generated with Claude Code

davidschachterADFA and others added 7 commits July 24, 2026 14:50
…ve equivalents

Adds Context.dpToPx()/spToPx() extensions to common's ContextUtils.kt,
matching blankj SizeUtils' rounding formula exactly, and swaps all 22
com.blankj.utilcode SizeUtils/ConvertUtils.dp2px call sites across app,
editor, uidesigner, xml-inflater, and common-ui to use them. First of
several staged commits removing the com.blankj:utilcodex dependency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…helpers

Adds a shared mainThreadHandler to common's TaskExecutor.kt and updates
its existing runOnUiThread() to post through it instead of blankj's
ThreadUtils. Swaps all com.blankj.utilcode ThreadUtils.runOnUiThread/
getMainHandler/runOnUiThreadDelayed call sites across app, lsp/models,
lsp/java, and uidesigner to use it - preserving the same Handler
instance for post/removeCallbacks pairs where cancellation depends on
it. Second of several staged commits removing com.blankj:utilcodex.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds native FileUtils/FileIOUtils objects to common's utils package,
matching blankj's method names/signatures (isUtf8, readFile2String,
writeFileFromString, listFilesInDirWithFilter, delete, rename,
getFileExtension, createOrExistsDir) so call sites only needed an
import swap, not a rewrite. Covers app, editor, xml-inflater, and
common modules. Third of several staged commits removing
com.blankj:utilcodex.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ive equivalents

Adds copyToClipboard/isNetworkConnected/isSoftInputVisible extensions
to common's ContextUtils.kt (the latter using WindowInsetsCompat's IME
visibility check instead of blankj's decor-view-height heuristic).
Updates the CloneRepositoryViewModelTest mock to stub the new
extension via mockkStatic on the facade class instead of blankj's
NetworkUtils object. Fourth of several staged commits removing
com.blankj:utilcodex.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…lents

- ThrowableUtils.getFullStackTrace -> Throwable.stackTraceToString() (Kotlin stdlib)
- ConvertUtils.byte2MemorySize/MemoryConstants -> inline division
- AppUtils.getAppVersionCode -> new Context.getAppVersionCode() extension
- DeviceUtils.getManufacturer/getModel/isEmulator/isDeviceRooted -> ported
  onto the project's own (same-name, same-package) DeviceUtils object
- CloseUtils.closeIO -> direct try/close
- ArrayUtils.contains -> IntStream.anyMatch
- StringUtils.isTrimEmpty -> inline null/trim check
- Utils.getApp() -> BaseApplication.baseInstance
- ActivityUtils.startActivity -> IDEApplication.instance.startActivity
- ActivityUtils.getTopActivity() -> new BaseApplication.foregroundActivity,
  tracked via a lifecycle-callbacks registration in BaseApplication.onCreate()
  (common module can't depend on IDEApplication in app); IDEApplication's own
  more precise Pre/Post-based tracker now overrides it

Fifth of several staged commits removing com.blankj:utilcodex.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…h native equivalents

Adds four new common-module utility objects, each a same-name drop-in
so call sites only needed an import swap:

- ReflectUtils: a compact fluent reflection helper (reflect/field/
  method/newInstance/get) covering exactly the subset used across
  lsp/java, javac-services, and xml-inflater - field lookup uses the
  field's declared type when wrapping, matching blankj's behavior
  bug-for-bug (verified via javap against the actual blankj bytecode)
  so existing call chains keep working identically.
- ImageUtils: isImage (BitmapFactory decode-bounds check), getBitmap,
  and getImageType (magic-number header sniffing for JPG/PNG/GIF/TIFF/
  BMP/ICO/WEBP).
- ResourceUtils: copyFileFromAssets (recursive directory-aware asset
  copy, matching blankj) and readAssets2String, both via
  BaseApplication.baseInstance.assets.
- ZipUtils: unzipFile using java.util.zip.ZipFile, with zip-slip
  path-traversal protection added as a safe hardening.

Sixth of several staged commits removing com.blankj:utilcodex.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Drops the utilcodex dependency declaration from all 12 modules that
had it (app, common, editor, uidesigner, xml-inflater, actions,
lsp/api, lsp/java, lsp/models, lsp/xml, subprojects/javac-services,
subprojects/flashbar) and its now-unused entries from
gradle/libs.versions.toml. Verified: zero com.blankj.utilcode source
references remain, :app:compileV8DebugKotlin/JavaWithJavac builds
clean, unit tests pass (190 tests, 0 failures) across app/common/
editor/xml-inflater/lsp-java, a full :app:assembleV8Debug succeeds,
and the resulting debug APK contains no blankj/utilcode classes.

Final commit removing com.blankj:utilcodex (AndroidUtilCode/blankj) -
duplicated AndroidX core, Kotlin stdlib, and coroutines functionality
that this app now covers with small native equivalents, shrinking the
APK.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

…y tracker

Per architecture review: common can't depend on app (IDEApplication),
so this base tracker exists for common-module consumers (FlashbarUtils);
IDEApplication's own pre-existing Pre/Post-callback tracker overrides
it and wins at runtime in the real app.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@davidschachterADFA

Copy link
Copy Markdown
Collaborator Author

Architecture review

Ran the architecture-review skill against this diff (92 first-party files vs stage). Docs read: ARCHITECTURE.md, ADRs 0001, 0003, 0005, 0006, 0009.

This is a mechanical dependency-removal — every file swaps a blankj Xxx.method() call for a new native equivalent or Kotlin/AndroidX stdlib call. No new screens, persistence, DI wiring, or dependencies were introduced, so I verified the rules the diff could plausibly violate rather than fanning out per-dimension.

Verdict Area Rule Source Note
common/app module boundary app depends inward; libraries never depend on app ARCHITECTURE.md → module map common's new FlashbarUtils.kt/urlManager.kt use BaseApplication.baseInstance (defined in common); app-only files (BuildInfoUtils.kt, GlitchTipDiagnosticsContext.kt, CrashEventSubscriber.kt) use IDEApplication.instance. Confirmed zero IDEApplication references under common/.
Dependency changes Dependency substitution / avoid new deps ADR 0003; ARCHITECTURE.md Net-negative: removes com.blankj:utilcodex entirely, adds zero new coordinates.
Touched ViewModels (CloneRepositoryViewModel, GitBottomSheetViewModel, FileManagerViewModel, EditorViewModel) UDF / sealed state (StateFlow<UiState>) ARCHITECTURE.md → State Management Diffed line-by-line: +/- hunks are identical content (spotless reformat) except the isolated utility-call swaps. Sealed state hierarchies and StateFlow wiring untouched.
⚠️ common/.../app/BaseApplication.kt Koin DI / no hand-rolled singletons ADR 0006 Added a registerActivityLifecycleCallbacks tracker for foregroundActivity, needed because common can't reach IDEApplication's existing near-identical tracker. Not a new pattern — mirrors IDEApplication's pre-existing Pre/Post-callback tracker, which overrides and wins at runtime — but it is a second, redundant registration. Addressed: added a doc comment on foregroundActivity explaining the duplication is module-boundary-driven and intentional (da37d47f3). Non-blocking; a future cleanup could hoist the tracker fully into BaseApplication and delete IDEApplication's copy.
Sealed state, @Parcelize, Compose-for-new-UI, ABI flavors, Room-vs-SQLite, :resources strings, system bars ADR 0009, 0005, 0001; ARCHITECTURE.md; REVIEW.md §7; CLAUDE.md None applicable — no new UI, persistence, modules, or user-facing strings in this diff.

Summary: 0 violations, 1 non-blocking warning (addressed with a doc comment), rest checked and clean.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request replaces BlankJ UtilCode with AndroidIDE utilities, migrates dependencies and callers across modules, updates file, threading, connectivity, crash, and editor flows, adds utility and connectivity tests, and records refactoring guidance.

Changes

AndroidIDE utility migration

Layer / File(s) Summary
Shared utility foundation
common/src/main/java/com/itsaky/androidide/utils/*, common/src/main/java/com/itsaky/androidide/tasks/*, common/src/test/*
Adds shared context, filesystem, image, reflection, resource, archive, device, task, and foreground-activity utilities with regression coverage.
Dependency and caller migration
*/build.gradle.kts, gradle/libs.versions.toml, app/src/main/*, editor/src/main/*, lsp/*, uidesigner/*, xml-inflater/*
Removes common.utilcode and migrates file, clipboard, density, keyboard, threading, reflection, resource, and connectivity call sites.
Behavior and flow updates
app/src/main/*, common-ui/src/main/*, lsp/*
Updates crash handling, editor opening, file operations, Git connectivity checks, completion dispatch, cancellation handling, and UI scheduling.
Process guidance and validation
CLAUDE.md, docs/process/*, app/src/test/*, common/src/test/*
Adds fast-iteration and staged-refactor guidance, retrospective notes, connectivity mocking updates, and utility regression tests.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested reviewers: dara-abijo-adfa, daniel-adfa, itsaky-adfa, jomen-adfa, jatezzz

Poem

A rabbit hops through cleaner code,
Replacing tools along the road.
Threads and files now neatly flow,
While tests keep watch below.
Old helpers nap beneath the moon—
A tidy build will follow soon!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.95% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: removing the AndroidUtilCode dependency.
Description check ✅ Passed The description is clearly aligned with the changes and explains the dependency removal and replacements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ADFA-4649-remove-blankj-utilcode

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.

@hal-eisen-adfa

Copy link
Copy Markdown
Collaborator

Code review: two blocking regressions in the blankj replacements

I diffed each new in-house util against the actual blankj 1.31.1 sources. Most substitutions are faithful, but two change behavior in ways that are reachable in normal use.

1. FileUtils.rename can silently overwrite (destroy) another file

common/.../utils/FileUtils.kt

fun rename(file: File, newName: String): Boolean =
    file.renameTo(File(file.parentFile, newName))

blankj's rename refused to clobber an existing target:

// the new name of file exists then return false
return !newFile.exists() && file.renameTo(newFile);

That guard is gone. The rename UI does not replace it: RenameAction only validates non-empty / length ≤ 40, then FileManagerViewModel.renameFile calls FileUtils.rename with no collision check.

Repro: a folder has a.txt and b.txt; the user renames a.txtb.txt. Previously rename returned false and the UI flashed "rename failed" (no loss). Now File.renameTo invokes Linux rename(2), which atomically replaces the destination — b.txt's contents are lost.

Fix: restore the guard, e.g. !dest.exists() && file.renameTo(dest) (or pre-check for a collision in renameFile and surface an error).

2. FileUtils.isUtf8 now reads and decodes the entire file (blankj read 24 bytes)

common/.../utils/FileUtils.kt

file.inputStream().use { input -> decoder.decode(ByteBuffer.wrap(input.readBytes())) }

input.readBytes() slurps the whole file. blankj sampled only the first 24 bytes:

byte[] bytes = new byte[24];
int read = is.read(bytes);
...
return isUtf8(readArr) == 100;

This runs on hot paths:

  • RecursiveFileSearcherisUtf8(file) per candidate in MultiFileFilter.accept, then the file is read again in full via readFile2String. Project-wide "find in files" now does ~2× full I/O plus a full UTF-8 decode per file (OOM risk on a large file).
  • BaseEditorActivity (tab restore, runs at startup) and IDELanguageClientImpl — one full read+decode per file.

It also changes classification: a file that is valid UTF-8 in its first 24 bytes but has an invalid byte deeper in (e.g. a mostly-ASCII file with a stray byte at offset 5000) was accepted by blankj and is now excluded from search / reopen; an empty file flips falsetrue.

Fix: decode only a header sample (read N bytes, decode those) to preserve the cheap-check semantics; if strict whole-file validation is actually wanted somewhere, make it an explicit separate helper off the hot paths.


The rest of the diff (dp/px, ThreadUtils→mainThreadHandler, ImageUtils, ZipUtils, ResourceUtils, ReflectUtils, clipboard, network, DeviceUtils) checks out against blankj. There are a few lower-severity notes (readFile2String now throws instead of returning null; isSoftInputVisible via WindowInsets.ime() is unreliable on API 28–29; isNetworkConnected narrowed to NET_CAPABILITY_INTERNET) — happy to file those separately if useful.

rename() silently overwrote an existing file at the destination since
File.renameTo invokes rename(2), which atomically replaces the target.
blankj's original guarded against this; restore the check.

isUtf8() read and decoded the entire file instead of sampling a small
header like blankj's original 24-byte check, turning a cheap classifier
call into a full read+decode on hot paths (RecursiveFileSearcher,
BaseEditorActivity tab restore, IDELanguageClientImpl). Sample only the
header again, and return false for empty files to match prior behavior.

Adds FileUtilsTest covering both fixes plus the header-sampling and
empty-file edge cases.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (3)
app/src/main/java/com/itsaky/androidide/app/CredentialProtectedApplicationLoader.kt (1)

75-85: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Allow initialization to retry after user unlock.

_isLoaded is set before isCredentialStorageReady(). If the app starts before credential storage is available, later calls return at Line 70 and credential-protected initialization never occurs. Set the loaded flag only after readiness succeeds, ideally with compareAndSet.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/src/main/java/com/itsaky/androidide/app/CredentialProtectedApplicationLoader.kt`
around lines 75 - 85, Update the loading flow in the credential-protected
application loader so _isLoaded is not set until after
isCredentialStorageReady(app) succeeds. Use compareAndSet to atomically claim
initialization after readiness, allowing a later call after user unlock to
retry; preserve the existing early return when storage is unavailable and set
application only for successful initialization.
app/src/main/java/com/itsaky/androidide/fragments/RunTasksDialogFragment.kt (1)

150-162: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Cancel delayed callbacks during teardown. Both callbacks outlive their UI owner and can apply stale state after dismissal or destruction.

  • app/src/main/java/com/itsaky/androidide/fragments/RunTasksDialogFragment.kt#L150-L162: retain the debounce runnable/text watcher and remove the callback in onDestroyView().
  • app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt#L1435-L1444: retain the onboarding runnable and remove it from mainThreadHandler in preDestroy().
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/itsaky/androidide/fragments/RunTasksDialogFragment.kt`
around lines 150 - 162, Retain the debounce Runnable and text watcher created in
RunTasksDialogFragment’s search listener, and remove the runnable from
mainThreadHandler during onDestroyView() before the view is released. In
BaseEditorActivity, retain the onboarding runnable around the affected startup
flow and remove it from mainThreadHandler in preDestroy().

Source: Coding guidelines

app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt (1)

409-435: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Rethrow lifecycle cancellation. Both lifecycleScope.launch blocks treat CancellationException as a restore failure, causing cancellation to be logged and suppressed. Rethrow it before handling parse failures, and narrow the remaining catch to expected JSON failures.

  • app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt#L409-L435: rethrow CancellationException before handling cached-file JSON parsing failures.
  • app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt#L445-L472: rethrow CancellationException before handling cached-plugin-tab JSON parsing failures.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt`
around lines 409 - 435, Update both lifecycleScope.launch blocks in
EditorHandlerActivity.kt at lines 409-435 and 445-472: add a
CancellationException catch that immediately rethrows before the existing
failure handling, then narrow the remaining catch to expected JSON parsing
failures for cached files and cached plugin tabs. Preserve the existing error
logging for those parsing failures.

Sources: Coding guidelines, Learnings

🧹 Nitpick comments (14)
common/src/main/java/com/itsaky/androidide/utils/ResourceUtils.kt (1)

37-56: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Catch only expected asset and stream failures.

These paths primarily throw IOException; catching Exception also converts unrelated state/programming defects into false or "". Keep the logged fallback, but narrow recovery to expected I/O failures (plus any explicitly intended recoverable security failure).

Also applies to: 63-71

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@common/src/main/java/com/itsaky/androidide/utils/ResourceUtils.kt` around
lines 37 - 56, Narrow the exception handling in the asset-copy logic around
copyFileFromAssets to catch only expected IOException failures, while preserving
the existing warning log and false return fallback. Apply the same restriction
to the related fallback path around the method’s lines 63-71, and do not convert
unrelated programming or state exceptions into recovery values unless an
explicitly intended security failure is handled.

Source: Coding guidelines

editor/src/main/java/com/itsaky/androidide/editor/ui/IDEEditor.kt (1)

378-378: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor: implicit default locale in log format string.

detekt flags String.format("Executing command '%s' for completion item.", command.title) for using the implicit default locale. Since this is only a debug/info log with a single %s, the practical impact is negligible.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@editor/src/main/java/com/itsaky/androidide/editor/ui/IDEEditor.kt` at line
378, Update the log formatting in the completion command execution flow to avoid
relying on the implicit default locale. Replace the String.format usage in the
visible log.info call with a locale-independent formatting approach while
preserving the existing message and command.title value.

Source: Linters/SAST tools

app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt (1)

516-524: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Use explicit Locale in String.format for archive listing.

detekt flags all three String.format(...) calls here for using the implicit default locale. Since these compose the numeric columns (sizes, CRC, compression ratio) shown to the user, output can vary by device locale (e.g. decimal separator in "%.1f%%"), producing inconsistent archive listings across devices.

♻️ Proposed fix
 				builder.appendLine(
 					String.format(
+						java.util.Locale.ROOT,
 						"%-10s %-10s %-6s %-8s %-20s %s",
 						"Length",
 						"Compressed",
 						"Method",
 						"CRC-32",
 						"Date & Time",
 						"Name",
 					),
 				)

Apply the same Locale.ROOT argument to the other two String.format calls at lines 549-557 and 569-575.

Also applies to: 549-557, 569-575

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt` around lines
516 - 524, Update all three archive-listing String.format calls in
CodeEditorView to pass Locale.ROOT explicitly, including the header formatting
call and the calls near the numeric size/CRC/compression output. Preserve the
existing format strings and arguments so listing output remains
locale-independent.

Source: Linters/SAST tools

common/src/main/java/com/itsaky/androidide/utils/ContextUtils.kt (1)

42-53: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Swallowed exceptions without logging.

isAccessibilityEnabled() (catch at line 50) and getAppVersionCode() (catch at line 122) both discard the caught exception without any logging, so a genuinely unexpected failure mode (e.g., unexpected SettingNotFoundException, or a package-lookup failure) would be silently invisible in production/GlitchTip.

♻️ Suggested fix (getAppVersionCode example)
 fun Context.getAppVersionCode(): Int =
 	try {
 		packageManager.getPackageInfo(packageName, 0).versionCode
 	} catch (e: PackageManager.NameNotFoundException) {
+		LoggerFactory.getLogger("ContextUtils").warn("Unable to resolve own package version code", e)
 		-1
 	}

As per coding guidelines: "Do not swallow exceptions silently; log handled notable failures and report them through the established observability mechanism when appropriate."

Also applies to: 115-125

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@common/src/main/java/com/itsaky/androidide/utils/ContextUtils.kt` around
lines 42 - 53, Update isAccessibilityEnabled and getAppVersionCode to log the
caught exceptions through the project’s established logging/observability
mechanism before returning their existing fallback values. Preserve the current
false/fallback behavior and avoid changing exception handling beyond making
these failures visible.

Sources: Coding guidelines, Linters/SAST tools

editor/src/main/java/com/itsaky/androidide/editor/language/groovy/GroovyAutoComplete.java (1)

354-379: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Hoist the lowercased prefix out of the loops.

prefix.toLowerCase(Locale.ROOT) is recomputed for every one of the ~300 candidates on each keystroke.

♻️ Proposed refactor
+		final var lowerPrefix = prefix.toLowerCase(Locale.ROOT);
 		for (String artifact : ANDROIDX_ARTIFACTS) {
-			if (!artifact.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT))) {
+			if (!artifact.toLowerCase(Locale.ROOT).startsWith(lowerPrefix)) {
 				continue;
 			}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@editor/src/main/java/com/itsaky/androidide/editor/language/groovy/GroovyAutoComplete.java`
around lines 354 - 379, Compute the lowercased prefix once before the
ANDROIDX_ARTIFACTS, CONFIGURATIONS, and OTHERS loops, then reuse that value in
each startsWith comparison instead of calling prefix.toLowerCase(Locale.ROOT)
per candidate.
app/src/main/java/com/itsaky/androidide/adapters/RecentProjectsAdapter.kt (1)

209-217: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Callback body is a no-op.

The executeAsync completion block only computes deleted and returns; it neither surfaces failure to the user nor refreshes anything. Either report the failure or drop the callback.

♻️ Suggested simplification
 				.setPositiveButton(R.string.yes) { _, _ ->
 					onRemoveProjectClick(project)
-					executeAsync({ FileUtils.delete(project.path) }) {
-						val deleted = it ?: false
-						if (!deleted) {
-							return@executeAsync
-						}
-					}
+					executeAsync({ FileUtils.delete(project.path) }) { deleted ->
+						if (deleted != true) {
+							flashError(R.string.delete_failed)
+						}
+					}
 				}.create()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/itsaky/androidide/adapters/RecentProjectsAdapter.kt`
around lines 209 - 217, Remove the no-op completion callback from the
executeAsync call in the setPositiveButton handler, unless a user-facing failure
message or project-list refresh is implemented there; avoid computing deleted
without taking any action.
app/src/main/java/com/itsaky/androidide/viewmodel/GitBottomSheetViewModel.kt (1)

182-188: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

ViewModel now depends on the BaseApplication singleton for connectivity.

Reaching into BaseApplication.baseInstance makes the offline branches of push/pull untestable without a Robolectric application. Consider injecting a small connectivity provider (e.g. () -> Boolean) alongside credentialsManager. Same pattern applies at Line 266 in pull.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/itsaky/androidide/viewmodel/GitBottomSheetViewModel.kt`
around lines 182 - 188, Replace direct BaseApplication.baseInstance connectivity
access in the push and pull flows with an injected connectivity provider, such
as a Boolean-returning function, alongside credentialsManager. Use that provider
for the offline checks in the relevant ViewModel methods and update
construction/call sites to supply it, preserving the existing
no_internet_connection error behavior.
xml-inflater/src/main/java/com/itsaky/androidide/inflater/vectormaster/VectorMasterDrawable.java (1)

58-62: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Catch I/O failures before fromXMLFile.

FileIOUtils.readFile2String(file) delegates to File.readText(Charsets.UTF_8), which throws unrecoverable I/O failures to callers. Update fromXMLFile to catch the I/O failure and return/propagate an explicit error state instead of letting callers receive an unexpected UncheckedIOException/crash.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@xml-inflater/src/main/java/com/itsaky/androidide/inflater/vectormaster/VectorMasterDrawable.java`
around lines 58 - 62, Update VectorMasterDrawable.fromXMLFile to catch the I/O
failure raised by FileIOUtils.readFile2String before invoking fromXML, then
return or propagate the established explicit error state rather than allowing an
unchecked I/O exception to escape. Preserve the existing XML parsing behavior
for successfully read files.

Source: Coding guidelines

common/src/main/java/com/itsaky/androidide/utils/ReflectUtils.kt (2)

106-115: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Dead elvis branch in newInstance.

Constructor.newInstance never returns null, so the ?: return ReflectUtils(Any::class.java, null) fallback is unreachable and, if it ever were reached, would hand back a wrapper with a lost target.

♻️ Suggested simplification
-			reflect(constructor.newInstance(*args) ?: return ReflectUtils(Any::class.java, null))
+			reflect(constructor.newInstance(*args))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@common/src/main/java/com/itsaky/androidide/utils/ReflectUtils.kt` around
lines 106 - 115, Remove the unreachable Elvis fallback from
ReflectUtils.newInstance and pass the result of constructor.newInstance(*args)
directly to reflect. Preserve the existing exception handling and constructor
accessibility behavior.

117-163: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add KDoc to the public fluent API.

field, field(name, value), method, and get are the public surface of a hand-rolled reflection helper; documenting the contract (throws ReflectException, method returns the receiver for void/null results, get() is an unchecked cast) would prevent misuse.

As per coding guidelines: "Public classes, functions, and non-obvious logic must have KDoc or Javadoc documenting contracts, rationale, threading, nullability, side effects, or units."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@common/src/main/java/com/itsaky/androidide/utils/ReflectUtils.kt` around
lines 117 - 163, Add KDoc to the public ReflectUtils APIs field(name),
field(name, value), method(name, vararg args), and get(). Document their
reflection behavior, ReflectException failure contract, method’s receiver return
for void or null results, and get()’s unchecked cast semantics without changing
implementation behavior.

Source: Coding guidelines

app/src/test/java/com/itsaky/androidide/viewmodel/CloneRepositoryViewModelTest.kt (1)

50-65: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer the typed mockkStatic overload and add an offline-path test.

The string form "com.itsaky.androidide.utils.ContextUtilsKt" hard-codes the generated file-class name; a file rename or @file:JvmName breaks the stub. MockK's function-reference overload is refactor-safe. Also, the newly introduced connectivity precondition has no test for the false branch (R.string.no_internet_connection + canRetry), which is the error path the guidelines ask to cover.

As per coding guidelines: "Use unit tests for non-UI logic, cover error and edge paths".

♻️ Suggested change
-		mockkStatic("com.itsaky.androidide.utils.ContextUtilsKt")
+		mockkStatic(Context::isNetworkConnected)
 		every { context.isNetworkConnected() } returns true

Plus a new test:

`@Test`
fun `clone is rejected when device is offline`() {
	every { context.isNetworkConnected() } returns false
	val folder = tempFolder.newFolder("Offline")

	viewModel.cloneRepository("https://github.com/username/newproject.git", folder.absolutePath)

	val state = viewModel.uiState.value
	assertTrue(state is CloneRepoUiState.Error)
	assertEquals(R.string.no_internet_connection, (state as CloneRepoUiState.Error).errorResId)
	assertTrue(state.canRetry)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/src/test/java/com/itsaky/androidide/viewmodel/CloneRepositoryViewModelTest.kt`
around lines 50 - 65, The setup in CloneRepositoryViewModelTest uses a brittle
generated file-class string and lacks coverage for the offline branch. Replace
the string-based mockkStatic call with the typed function-reference overload for
isNetworkConnected, then add a test covering cloneRepository when connectivity
returns false that asserts CloneRepoUiState.Error,
R.string.no_internet_connection, and canRetry true.

Source: Coding guidelines

common/src/main/java/com/itsaky/androidide/utils/ZipUtils.kt (1)

30-64: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add unit tests for the new unzipFile, especially the zip-slip rejection.

The traversal guard is the security-relevant part of this replacement and there is no ZipUtilsTest in this PR (only FileUtilsTest). A small JUnit test writing a zip with a ../evil.txt entry plus a happy-path/nested-directory case would lock the behavior in.

As per coding guidelines: "Use unit tests for non-UI logic, cover error and edge paths, and target at least 50% line and branch coverage for new or changed non-UI code."

Want me to generate common/src/test/java/com/itsaky/androidide/utils/ZipUtilsTest.kt?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@common/src/main/java/com/itsaky/androidide/utils/ZipUtils.kt` around lines 30
- 64, The new unzipFile behavior lacks unit coverage, particularly for path
traversal rejection. Add a ZipUtilsTest test class covering a successful
extraction with nested directories and a zip containing ../evil.txt that causes
unzipFile to throw IOException; use temporary files/directories and assert the
expected extracted content and security failure.

Source: Coding guidelines

lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CompletionProvider.java (1)

253-279: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Drop reflection from the provider switch.

The switch already maps each Tree.Kind to a concrete completion provider, and the referenced providers already provide constructors accepting those arguments. Instantiating via ReflectUtils.reflect(klass).newInstance(...) defers a constructor mismatch until runtime and adds a reflection dependency for work that can be checked at compile time by returning the provider directly from each case.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CompletionProvider.java`
around lines 253 - 279, Replace the reflective provider construction in
CompletionProvider with direct instantiation in each Tree.Kind switch case,
returning the corresponding concrete provider with file, cursor, compiler, and
getSettings() arguments. Remove the klass variable and ReflectUtils usage while
preserving the existing default KeywordCompletionProvider mapping and
cancellation check.
common/src/main/java/com/itsaky/androidide/utils/FileUtils.kt (1)

27-112: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the new public utility contracts.

  • common/src/main/java/com/itsaky/androidide/utils/FileUtils.kt#L27-L112: add KDoc for the public objects and methods, especially UTF-8 sampling, recursive filtering, deletion, rename, and write-result semantics.
  • common/src/main/java/com/itsaky/androidide/utils/ImageUtils.kt#L25-L60: add KDoc for ImageUtils and ImageType, including header-sniffing limitations.

As per coding guidelines, “Public classes, functions, and non-obvious logic must have KDoc or Javadoc documenting contracts.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@common/src/main/java/com/itsaky/androidide/utils/FileUtils.kt` around lines
27 - 112, Document the public objects and methods in
common/src/main/java/com/itsaky/androidide/utils/FileUtils.kt:27-112 with KDoc,
covering UTF-8 header sampling, recursive filtering, deletion, rename behavior,
and write-result semantics; document
common/src/main/java/com/itsaky/androidide/utils/ImageUtils.kt:25-60 with KDoc
for ImageUtils and ImageType, including header-sniffing limitations. Use the
existing symbols FileUtils, FileIOUtils, ImageUtils, and ImageType, with no
behavioral changes required.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/com/itsaky/androidide/actions/FileActionManager.kt`:
- Around line 23-37: Narrow exception handling across the specified recovery
paths: in FileActionManager’s coroutine wrapper and the three NewFileAction
UI-action paths, rethrow coroutine cancellation before handling only expected
file, UI/I/O, or parsing failures; replace broad Exception catches and top-level
runCatching accordingly. In CredentialProtectedApplicationLoader’s WorkManager
initialization path, replace runCatching around WorkManager.getInstance with a
catch for the expected recoverable failure, without adding cancellation handling
there.

In `@app/src/main/java/com/itsaky/androidide/adapters/RecentProjectsAdapter.kt`:
- Around line 251-267: The rename handler in the positive-button callback
performs project.rename disk I/O on the main thread. Route the rename operation
through the existing executeAsync pattern used by the delete flow, and move
flashSuccess, onFileRenamed, notifyItemChanged, and error handling into the
appropriate completion callback while preserving the existing rename arguments
and user-facing results.

In `@app/src/main/java/com/itsaky/androidide/utils/TemplateRecipeExecutor.kt`:
- Around line 64-71: Update copyAsset to manage the destination stream with its
own use block alongside the openAsset input stream, ensuring both streams are
closed after copying while preserving the existing copy behavior.

In `@app/src/main/java/com/itsaky/androidide/viewmodel/FileManagerViewModel.kt`:
- Around line 42-51: Update the case-only rename branch in the renamed
computation to roll back the temporary rename when tempFile.renameTo(destFile)
fails: attempt to rename tempFile back to file before returning failure.
Preserve the existing success path and non-case-only FileUtils.rename behavior.

In `@common/src/main/java/com/itsaky/androidide/utils/FileUtils.kt`:
- Around line 46-47: Replace broad exception handling with IOException-only
catches in FileUtils.kt lines 46-47 and 109-110, preserving false results; log
the IOException at the write/failure site before returning false at lines
109-110. In ImageUtils.kt lines 67-69, catch IOException only and preserve the
TYPE_UNKNOWN result; allow unrelated runtime exceptions to propagate.

In `@common/src/main/java/com/itsaky/androidide/utils/ImageUtils.kt`:
- Line 81: Update the TIFF branch in the image-type detection logic to require
the complete signature: little-endian headers must be II 2A 00 and big-endian
headers must be MM 00 2A, rather than validating only the byte-order markers.
Preserve other format checks and add coverage for both valid TIFF headers and
invalid II/MM-prefixed headers.

In `@common/src/test/java/com/itsaky/androidide/utils/FileUtilsTest.kt`:
- Around line 4-6: Update the new tests in FileUtilsTest to use JUnit Jupiter
annotations and replace the JUnit 4 Rule/TemporaryFolder setup with the Jupiter
temporary-directory extension. Remove the org.junit.Rule and
org.junit.rules.TemporaryFolder imports, use the Jupiter equivalents, and adapt
the test fixture to receive the extension-provided temporary directory.

In
`@editor/src/main/java/com/itsaky/androidide/editor/language/incremental/BaseIncrementalAnalyzeManager.java`:
- Around line 389-392: Update the token-type check in the incremental analysis
loop to use the cached blockTokens field instead of calling getCodeBlockTokens()
and creating an IntStream for each token. Preserve null-safe behavior so a null
blockTokens value is treated as no matching code-block token, and remove the
now-unused IntStream import.

---

Outside diff comments:
In
`@app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt`:
- Around line 409-435: Update both lifecycleScope.launch blocks in
EditorHandlerActivity.kt at lines 409-435 and 445-472: add a
CancellationException catch that immediately rethrows before the existing
failure handling, then narrow the remaining catch to expected JSON parsing
failures for cached files and cached plugin tabs. Preserve the existing error
logging for those parsing failures.

In
`@app/src/main/java/com/itsaky/androidide/app/CredentialProtectedApplicationLoader.kt`:
- Around line 75-85: Update the loading flow in the credential-protected
application loader so _isLoaded is not set until after
isCredentialStorageReady(app) succeeds. Use compareAndSet to atomically claim
initialization after readiness, allowing a later call after user unlock to
retry; preserve the existing early return when storage is unavailable and set
application only for successful initialization.

In `@app/src/main/java/com/itsaky/androidide/fragments/RunTasksDialogFragment.kt`:
- Around line 150-162: Retain the debounce Runnable and text watcher created in
RunTasksDialogFragment’s search listener, and remove the runnable from
mainThreadHandler during onDestroyView() before the view is released. In
BaseEditorActivity, retain the onboarding runnable around the affected startup
flow and remove it from mainThreadHandler in preDestroy().

---

Nitpick comments:
In `@app/src/main/java/com/itsaky/androidide/adapters/RecentProjectsAdapter.kt`:
- Around line 209-217: Remove the no-op completion callback from the
executeAsync call in the setPositiveButton handler, unless a user-facing failure
message or project-list refresh is implemented there; avoid computing deleted
without taking any action.

In `@app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt`:
- Around line 516-524: Update all three archive-listing String.format calls in
CodeEditorView to pass Locale.ROOT explicitly, including the header formatting
call and the calls near the numeric size/CRC/compression output. Preserve the
existing format strings and arguments so listing output remains
locale-independent.

In
`@app/src/main/java/com/itsaky/androidide/viewmodel/GitBottomSheetViewModel.kt`:
- Around line 182-188: Replace direct BaseApplication.baseInstance connectivity
access in the push and pull flows with an injected connectivity provider, such
as a Boolean-returning function, alongside credentialsManager. Use that provider
for the offline checks in the relevant ViewModel methods and update
construction/call sites to supply it, preserving the existing
no_internet_connection error behavior.

In
`@app/src/test/java/com/itsaky/androidide/viewmodel/CloneRepositoryViewModelTest.kt`:
- Around line 50-65: The setup in CloneRepositoryViewModelTest uses a brittle
generated file-class string and lacks coverage for the offline branch. Replace
the string-based mockkStatic call with the typed function-reference overload for
isNetworkConnected, then add a test covering cloneRepository when connectivity
returns false that asserts CloneRepoUiState.Error,
R.string.no_internet_connection, and canRetry true.

In `@common/src/main/java/com/itsaky/androidide/utils/ContextUtils.kt`:
- Around line 42-53: Update isAccessibilityEnabled and getAppVersionCode to log
the caught exceptions through the project’s established logging/observability
mechanism before returning their existing fallback values. Preserve the current
false/fallback behavior and avoid changing exception handling beyond making
these failures visible.

In `@common/src/main/java/com/itsaky/androidide/utils/FileUtils.kt`:
- Around line 27-112: Document the public objects and methods in
common/src/main/java/com/itsaky/androidide/utils/FileUtils.kt:27-112 with KDoc,
covering UTF-8 header sampling, recursive filtering, deletion, rename behavior,
and write-result semantics; document
common/src/main/java/com/itsaky/androidide/utils/ImageUtils.kt:25-60 with KDoc
for ImageUtils and ImageType, including header-sniffing limitations. Use the
existing symbols FileUtils, FileIOUtils, ImageUtils, and ImageType, with no
behavioral changes required.

In `@common/src/main/java/com/itsaky/androidide/utils/ReflectUtils.kt`:
- Around line 106-115: Remove the unreachable Elvis fallback from
ReflectUtils.newInstance and pass the result of constructor.newInstance(*args)
directly to reflect. Preserve the existing exception handling and constructor
accessibility behavior.
- Around line 117-163: Add KDoc to the public ReflectUtils APIs field(name),
field(name, value), method(name, vararg args), and get(). Document their
reflection behavior, ReflectException failure contract, method’s receiver return
for void or null results, and get()’s unchecked cast semantics without changing
implementation behavior.

In `@common/src/main/java/com/itsaky/androidide/utils/ResourceUtils.kt`:
- Around line 37-56: Narrow the exception handling in the asset-copy logic
around copyFileFromAssets to catch only expected IOException failures, while
preserving the existing warning log and false return fallback. Apply the same
restriction to the related fallback path around the method’s lines 63-71, and do
not convert unrelated programming or state exceptions into recovery values
unless an explicitly intended security failure is handled.

In `@common/src/main/java/com/itsaky/androidide/utils/ZipUtils.kt`:
- Around line 30-64: The new unzipFile behavior lacks unit coverage,
particularly for path traversal rejection. Add a ZipUtilsTest test class
covering a successful extraction with nested directories and a zip containing
../evil.txt that causes unzipFile to throw IOException; use temporary
files/directories and assert the expected extracted content and security
failure.

In
`@editor/src/main/java/com/itsaky/androidide/editor/language/groovy/GroovyAutoComplete.java`:
- Around line 354-379: Compute the lowercased prefix once before the
ANDROIDX_ARTIFACTS, CONFIGURATIONS, and OTHERS loops, then reuse that value in
each startsWith comparison instead of calling prefix.toLowerCase(Locale.ROOT)
per candidate.

In `@editor/src/main/java/com/itsaky/androidide/editor/ui/IDEEditor.kt`:
- Line 378: Update the log formatting in the completion command execution flow
to avoid relying on the implicit default locale. Replace the String.format usage
in the visible log.info call with a locale-independent formatting approach while
preserving the existing message and command.title value.

In
`@lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CompletionProvider.java`:
- Around line 253-279: Replace the reflective provider construction in
CompletionProvider with direct instantiation in each Tree.Kind switch case,
returning the corresponding concrete provider with file, cursor, compiler, and
getSettings() arguments. Remove the klass variable and ReflectUtils usage while
preserving the existing default KeywordCompletionProvider mapping and
cancellation check.

In
`@xml-inflater/src/main/java/com/itsaky/androidide/inflater/vectormaster/VectorMasterDrawable.java`:
- Around line 58-62: Update VectorMasterDrawable.fromXMLFile to catch the I/O
failure raised by FileIOUtils.readFile2String before invoking fromXML, then
return or propagate the established explicit error state rather than allowing an
unchecked I/O exception to escape. Preserve the existing XML parsing behavior
for successfully read files.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5f893600-589c-4eca-93e5-647e3e3688af

📥 Commits

Reviewing files that changed from the base of the PR and between 7242301 and 90ea7f4.

📒 Files selected for processing (97)
  • CLAUDE.md
  • actions/build.gradle.kts
  • app/build.gradle.kts
  • app/src/main/java/com/itsaky/androidide/actions/FileActionManager.kt
  • app/src/main/java/com/itsaky/androidide/actions/filetree/CopyPathAction.kt
  • app/src/main/java/com/itsaky/androidide/actions/filetree/DeleteAction.kt
  • app/src/main/java/com/itsaky/androidide/actions/filetree/NewFileAction.kt
  • app/src/main/java/com/itsaky/androidide/actions/text/RedoAction.kt
  • app/src/main/java/com/itsaky/androidide/activities/AboutActivity.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/FullscreenManager.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/ProjectHandlerActivity.kt
  • app/src/main/java/com/itsaky/androidide/adapters/RecentProjectsAdapter.kt
  • app/src/main/java/com/itsaky/androidide/adapters/SearchListAdapter.kt
  • app/src/main/java/com/itsaky/androidide/adapters/TemplateListAdapter.kt
  • app/src/main/java/com/itsaky/androidide/adapters/onboarding/OnboardingPermissionsAdapter.kt
  • app/src/main/java/com/itsaky/androidide/adapters/viewholders/FileTreeViewHolder.java
  • app/src/main/java/com/itsaky/androidide/app/CredentialProtectedApplicationLoader.kt
  • app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt
  • app/src/main/java/com/itsaky/androidide/fragments/RunTasksDialogFragment.kt
  • app/src/main/java/com/itsaky/androidide/fragments/SearchFieldToolbar.kt
  • app/src/main/java/com/itsaky/androidide/fragments/sheets/OptionsListFragment.java
  • app/src/main/java/com/itsaky/androidide/fragments/sidebar/FileTreeFragment.kt
  • app/src/main/java/com/itsaky/androidide/handlers/CrashEventSubscriber.kt
  • app/src/main/java/com/itsaky/androidide/handlers/GlitchTipDiagnosticsContext.kt
  • app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java
  • app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.kt
  • app/src/main/java/com/itsaky/androidide/tasks/callables/ListDirectoryCallable.java
  • app/src/main/java/com/itsaky/androidide/tasks/callables/UnzipCallable.java
  • app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt
  • app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt
  • app/src/main/java/com/itsaky/androidide/utils/BuildInfoUtils.kt
  • app/src/main/java/com/itsaky/androidide/utils/IntentUtils.kt
  • app/src/main/java/com/itsaky/androidide/utils/ProjectWriter.java
  • app/src/main/java/com/itsaky/androidide/utils/RecursiveFileSearcher.java
  • app/src/main/java/com/itsaky/androidide/utils/TemplateRecipeExecutor.kt
  • app/src/main/java/com/itsaky/androidide/utils/WindowInsetsExtensions.kt
  • app/src/main/java/com/itsaky/androidide/viewmodel/CloneRepositoryViewModel.kt
  • app/src/main/java/com/itsaky/androidide/viewmodel/EditorViewModel.kt
  • app/src/main/java/com/itsaky/androidide/viewmodel/FileManagerViewModel.kt
  • app/src/main/java/com/itsaky/androidide/viewmodel/GitBottomSheetViewModel.kt
  • app/src/test/java/com/itsaky/androidide/viewmodel/CloneRepositoryViewModelTest.kt
  • common-ui/src/main/java/com/itsaky/androidide/FabPositionCalculator.kt
  • common/build.gradle.kts
  • common/src/main/java/com/itsaky/androidide/app/BaseApplication.kt
  • common/src/main/java/com/itsaky/androidide/managers/ToolsManager.java
  • common/src/main/java/com/itsaky/androidide/tasks/TaskExecutor.kt
  • common/src/main/java/com/itsaky/androidide/utils/ContextUtils.kt
  • common/src/main/java/com/itsaky/androidide/utils/DeviceUtils.kt
  • common/src/main/java/com/itsaky/androidide/utils/Environment.java
  • common/src/main/java/com/itsaky/androidide/utils/FileUtils.kt
  • common/src/main/java/com/itsaky/androidide/utils/FlashbarUtils.kt
  • common/src/main/java/com/itsaky/androidide/utils/ImageUtils.kt
  • common/src/main/java/com/itsaky/androidide/utils/ReflectUtils.kt
  • common/src/main/java/com/itsaky/androidide/utils/ResourceUtils.kt
  • common/src/main/java/com/itsaky/androidide/utils/ZipUtils.kt
  • common/src/main/java/com/itsaky/androidide/utils/urlManager.kt
  • common/src/test/java/com/itsaky/androidide/utils/FileUtilsTest.kt
  • docs/process/learnings.md
  • docs/process/retrospective.md
  • editor/build.gradle.kts
  • editor/src/main/java/com/itsaky/androidide/editor/language/groovy/GroovyAutoComplete.java
  • editor/src/main/java/com/itsaky/androidide/editor/language/incremental/BaseIncrementalAnalyzeManager.java
  • editor/src/main/java/com/itsaky/androidide/editor/ui/BaseEditorWindow.java
  • editor/src/main/java/com/itsaky/androidide/editor/ui/EditorActionsMenu.kt
  • editor/src/main/java/com/itsaky/androidide/editor/ui/IDEEditor.kt
  • gradle/libs.versions.toml
  • lsp/api/build.gradle.kts
  • lsp/java/build.gradle.kts
  • lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/FieldBasedAction.kt
  • lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateConstructorAction.kt
  • lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateSettersAndGettersAction.kt
  • lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/GenerateToStringMethodAction.kt
  • lsp/java/src/main/java/com/itsaky/androidide/lsp/java/actions/generators/OverrideSuperclassMethodsAction.kt
  • lsp/java/src/main/java/com/itsaky/androidide/lsp/java/compiler/SourceFileManager.java
  • lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CompletionProvider.java
  • lsp/models/build.gradle.kts
  • lsp/models/src/main/java/com/itsaky/androidide/lsp/edits/DefaultEditHandler.kt
  • lsp/xml/build.gradle.kts
  • subprojects/flashbar/build.gradle.kts
  • subprojects/javac-services/build.gradle.kts
  • subprojects/javac-services/src/main/java/com/itsaky/androidide/javac/services/visitors/UnEnter.kt
  • uidesigner/build.gradle.kts
  • uidesigner/src/main/java/com/itsaky/androidide/uidesigner/drawable/UiViewLayeredForeground.kt
  • uidesigner/src/main/java/com/itsaky/androidide/uidesigner/fragments/DesignerWorkspaceFragment.kt
  • uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/ValueCompletionProvider.kt
  • uidesigner/src/main/java/com/itsaky/androidide/uidesigner/utils/ViewToXml.kt
  • uidesigner/src/main/java/com/itsaky/androidide/uidesigner/views/LayoutHierarchyView.kt
  • xml-inflater/build.gradle.kts
  • xml-inflater/src/main/java/com/itsaky/androidide/inflater/drawable/DrawableParserFactory.java
  • xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/GestureOverlayViewAdapter.kt
  • xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ListViewAdapter.kt
  • xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/TextViewAdapter.kt
  • xml-inflater/src/main/java/com/itsaky/androidide/inflater/internal/adapters/ToggleButtonAdapter.kt
  • xml-inflater/src/main/java/com/itsaky/androidide/inflater/models/UiWidget.kt
  • xml-inflater/src/main/java/com/itsaky/androidide/inflater/vectormaster/VectorMasterDrawable.java
💤 Files with no reviewable changes (13)
  • subprojects/javac-services/build.gradle.kts
  • subprojects/flashbar/build.gradle.kts
  • uidesigner/build.gradle.kts
  • lsp/models/build.gradle.kts
  • lsp/java/build.gradle.kts
  • lsp/xml/build.gradle.kts
  • xml-inflater/build.gradle.kts
  • editor/build.gradle.kts
  • common/build.gradle.kts
  • actions/build.gradle.kts
  • lsp/api/build.gradle.kts
  • app/build.gradle.kts
  • gradle/libs.versions.toml

Comment thread common/src/main/java/com/itsaky/androidide/utils/FileUtils.kt Outdated
Comment thread common/src/main/java/com/itsaky/androidide/utils/ImageUtils.kt Outdated
Comment thread common/src/test/java/com/itsaky/androidide/utils/FileUtilsTest.kt
davidschachterADFA and others added 2 commits July 27, 2026 13:10
Correctness fixes:
- CredentialProtectedApplicationLoader: don't mark initialization as
  loaded until credential storage readiness succeeds, so a failed
  attempt can retry after user unlock (compareAndSet-based claim).
- RecentProjectsAdapter: move project rename off the main thread
  (executeAsyncProvideError, matching the existing delete flow).
- TemplateRecipeExecutor: close the destination stream in copyAsset
  (was leaking a file descriptor per copy).
- FileManagerViewModel: roll back a case-only rename to its original
  name if the second rename step fails, instead of stranding the file
  under its temporary name.
- ImageUtils: fix TIFF detection to require the full 4-byte magic
  number, not just the 2-byte byte-order marker (was false-positiving
  on any file starting with "II"/"MM").
- ImageUtils.isUtf8-adjacent FileUtils.isUtf8: decode the UTF-8 sample
  with endOfInput=false so a multi-byte sequence truncated at the
  24-byte sample boundary reports underflow instead of a spurious
  malformed-input error.
- ReflectUtils: remove a dead Elvis fallback in newInstance, and
  restore blankj's final-field-modifier stripping in the field setter
  (best-effort; falls back to the normal IllegalAccessException where
  the runtime blocks it).
- CompletionProvider: replace reflective provider construction with
  direct instantiation per Tree.Kind (drops ReflectUtils from the
  completion hot path).
- VectorMasterDrawable.fromXMLFile: throw a clear IOException instead
  of passing a null read result into the XML parser.
- BaseIncrementalAnalyzeManager: use the already-cached blockTokens
  field instead of rebuilding an IntStream per token in the analysis
  loop.

Exception handling:
- FileActionManager, NewFileAction, EditorHandlerActivity: rethrow
  CancellationException before narrower catches, so cancellation
  isn't swallowed by structured concurrency.
- FileUtils, ImageUtils, ResourceUtils: narrow broad Exception catches
  to IOException; FileUtils/ResourceUtils now log on failure.

Other fixes:
- RunTasksDialogFragment, BaseEditorActivity: retain debounce/
  onboarding Runnables as stable fields and remove their callbacks in
  onDestroyView()/preDestroy(), since Handler.removeCallbacks needs
  the exact same Runnable instance to actually cancel anything.
- GitBottomSheetViewModel: make the connectivity check injectable
  (default arg preserves production behavior) for testability.
- CodeEditorView, IDEEditor, GroovyAutoComplete: locale-independent
  formatting for archive listing/logging, and hoist a loop-invariant
  toLowerCase call out of a per-candidate autocomplete loop.
- ContextUtils: log the exceptions isAccessibilityEnabled and
  getAppVersionCode were silently swallowing.

Test coverage:
- Add regression tests for all of the above where practical.
- Add ReflectUtilsTest, ImageUtilsTest, ResourceUtilsTest (new mockk
  test dependency in :common) covering previously-untested public API
  surface, and expand ZipUtils/FileUtils test coverage.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
common/src/test/java/com/itsaky/androidide/utils/FileUtilsTest.kt (1)

12-14: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Migrate these new tests to JUnit Jupiter.

These tests introduce JUnit 4 APIs despite the required Jupiter test stack. This repeats the prior FileUtilsTest finding.

  • common/src/test/java/com/itsaky/androidide/utils/FileUtilsTest.kt#L12-L14: replace TemporaryFolder with Jupiter @TempDir.
  • common/src/test/java/com/itsaky/androidide/utils/ImageUtilsTest.kt#L4-L6: replace JUnit 4 @Test and TemporaryFolder.
  • common/src/test/java/com/itsaky/androidide/utils/ReflectUtilsTest.kt#L4-L5: replace JUnit 4 @Test and Assert.assertThrows.
  • common/src/test/java/com/itsaky/androidide/utils/ResourceUtilsTest.kt#L10-L14: replace JUnit 4 lifecycle annotations and TemporaryFolder.
  • common/src/test/java/com/itsaky/androidide/utils/ZipUtilsTest.kt#L4-L7: replace JUnit 4 @Test, assertThrows, and TemporaryFolder.

As per coding guidelines, “Use JUnit Jupiter, Truth, MockK for new tests.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@common/src/test/java/com/itsaky/androidide/utils/FileUtilsTest.kt` around
lines 12 - 14, Migrate the listed tests to JUnit Jupiter: in
common/src/test/java/com/itsaky/androidide/utils/FileUtilsTest.kt (12-14),
replace TemporaryFolder with `@TempDir`; in ImageUtilsTest.kt (4-6), replace JUnit
4 `@Test` and TemporaryFolder; in ReflectUtilsTest.kt (4-5), replace JUnit 4 `@Test`
and Assert.assertThrows; in ResourceUtilsTest.kt (10-14), replace JUnit 4
lifecycle annotations and TemporaryFolder; and in ZipUtilsTest.kt (4-7), replace
JUnit 4 `@Test`, assertThrows, and TemporaryFolder. Use Jupiter, Truth, and MockK
APIs consistently while preserving each test’s behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@app/src/main/java/com/itsaky/androidide/app/CredentialProtectedApplicationLoader.kt`:
- Around line 82-89: Update the load method’s compareAndSet claim and
initialization flow so failures or cancellation during WorkManager, EventBus,
theme, or plugin setup do not leave _isLoaded true. Track initialization
separately or reset the claim and clean up partial registrations on failure, and
publish _isLoaded only after all initialization completes successfully; preserve
the existing concurrent-load guard.

In `@common/src/main/java/com/itsaky/androidide/utils/FileUtils.kt`:
- Around line 138-140: The IOException handler’s logger.warn call currently
exposes the full file path and potentially path details from the throwable.
Update the catch block to log only a path-free, redacted error identifier or
message, while preserving the false return behavior.

---

Outside diff comments:
In `@common/src/test/java/com/itsaky/androidide/utils/FileUtilsTest.kt`:
- Around line 12-14: Migrate the listed tests to JUnit Jupiter: in
common/src/test/java/com/itsaky/androidide/utils/FileUtilsTest.kt (12-14),
replace TemporaryFolder with `@TempDir`; in ImageUtilsTest.kt (4-6), replace JUnit
4 `@Test` and TemporaryFolder; in ReflectUtilsTest.kt (4-5), replace JUnit 4 `@Test`
and Assert.assertThrows; in ResourceUtilsTest.kt (10-14), replace JUnit 4
lifecycle annotations and TemporaryFolder; and in ZipUtilsTest.kt (4-7), replace
JUnit 4 `@Test`, assertThrows, and TemporaryFolder. Use Jupiter, Truth, and MockK
APIs consistently while preserving each test’s behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a4a15a41-e76d-46f8-829a-c21d95602d26

📥 Commits

Reviewing files that changed from the base of the PR and between 90ea7f4 and 5be744e.

📒 Files selected for processing (28)
  • app/src/main/java/com/itsaky/androidide/actions/FileActionManager.kt
  • app/src/main/java/com/itsaky/androidide/actions/filetree/NewFileAction.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt
  • app/src/main/java/com/itsaky/androidide/adapters/RecentProjectsAdapter.kt
  • app/src/main/java/com/itsaky/androidide/app/CredentialProtectedApplicationLoader.kt
  • app/src/main/java/com/itsaky/androidide/fragments/RunTasksDialogFragment.kt
  • app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt
  • app/src/main/java/com/itsaky/androidide/utils/TemplateRecipeExecutor.kt
  • app/src/main/java/com/itsaky/androidide/viewmodel/FileManagerViewModel.kt
  • app/src/main/java/com/itsaky/androidide/viewmodel/GitBottomSheetViewModel.kt
  • app/src/test/java/com/itsaky/androidide/viewmodel/CloneRepositoryViewModelTest.kt
  • common/build.gradle.kts
  • common/src/main/java/com/itsaky/androidide/utils/ContextUtils.kt
  • common/src/main/java/com/itsaky/androidide/utils/FileUtils.kt
  • common/src/main/java/com/itsaky/androidide/utils/ImageUtils.kt
  • common/src/main/java/com/itsaky/androidide/utils/ReflectUtils.kt
  • common/src/main/java/com/itsaky/androidide/utils/ResourceUtils.kt
  • common/src/test/java/com/itsaky/androidide/utils/FileUtilsTest.kt
  • common/src/test/java/com/itsaky/androidide/utils/ImageUtilsTest.kt
  • common/src/test/java/com/itsaky/androidide/utils/ReflectUtilsTest.kt
  • common/src/test/java/com/itsaky/androidide/utils/ResourceUtilsTest.kt
  • common/src/test/java/com/itsaky/androidide/utils/ZipUtilsTest.kt
  • editor/src/main/java/com/itsaky/androidide/editor/language/groovy/GroovyAutoComplete.java
  • editor/src/main/java/com/itsaky/androidide/editor/language/incremental/BaseIncrementalAnalyzeManager.java
  • editor/src/main/java/com/itsaky/androidide/editor/ui/IDEEditor.kt
  • lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/CompletionProvider.java
  • xml-inflater/src/main/java/com/itsaky/androidide/inflater/vectormaster/VectorMasterDrawable.java
🚧 Files skipped from review as they are similar to previous changes (14)
  • common/src/main/java/com/itsaky/androidide/utils/ResourceUtils.kt
  • common/build.gradle.kts
  • app/src/main/java/com/itsaky/androidide/fragments/RunTasksDialogFragment.kt
  • app/src/main/java/com/itsaky/androidide/viewmodel/FileManagerViewModel.kt
  • app/src/main/java/com/itsaky/androidide/utils/TemplateRecipeExecutor.kt
  • app/src/test/java/com/itsaky/androidide/viewmodel/CloneRepositoryViewModelTest.kt
  • editor/src/main/java/com/itsaky/androidide/editor/language/groovy/GroovyAutoComplete.java
  • app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt
  • app/src/main/java/com/itsaky/androidide/actions/filetree/NewFileAction.kt
  • common/src/main/java/com/itsaky/androidide/utils/ContextUtils.kt
  • app/src/main/java/com/itsaky/androidide/adapters/RecentProjectsAdapter.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt
  • editor/src/main/java/com/itsaky/androidide/editor/ui/IDEEditor.kt
  • app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt

Comment thread common/src/main/java/com/itsaky/androidide/utils/FileUtils.kt
davidschachterADFA and others added 3 commits July 27, 2026 16:45
CredentialProtectedApplicationLoader: _isLoaded was claimed via
compareAndSet before ~10 subsequent init steps (WorkManager, Environment,
FeatureFlags, EventBus, Termux, theme, plugin system, ToolsManager), several
of which had no error handling and would leave _isLoaded stuck true on
failure/cancellation, permanently blocking any retry. Wrap the init body in
try/catch that un-claims _isLoaded before rethrowing, and make EventBus
registration idempotent so a retry after a partial failure doesn't
immediately fail on double-registration.

FileUtils.writeFileFromString: the failure log included the file's full
path and the raw exception (whose message often re-embeds the path). Log
only the exception's class name instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…devforall/CodeOnTheGo into fix/ADFA-4649-remove-blankj-utilcode

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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)
app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt (1)

284-290: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Apply word-wrap to the active fragment when clicked.

This handler only changes the preference and button state. It does not call setWordWrapEnabled() on the active WrappableOutputFragment, so the output can remain unchanged until another sheet-state update occurs.

Proposed fix
 			binding.wordWrapOutputAction.setOnClickListener {
 				val newState = !EditorPreferences.outputWordWrap
 				EditorPreferences.outputWordWrap = newState
+				val fragment =
+					pagerAdapter.getFragmentAtIndex<Fragment>(binding.tabs.selectedTabPosition)
+				if (fragment is WrappableOutputFragment) {
+					fragment.setWordWrapEnabled(newState)
+				}
 				updateWordWrapButtonState(newState)
 			}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt` around lines
284 - 290, Update the wordWrapOutputAction click handler to apply the new state
immediately to the active WrappableOutputFragment by calling
setWordWrapEnabled(newState), while preserving the existing preference and
button-state updates.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt`:
- Around line 284-290: Update the wordWrapOutputAction click handler to apply
the new state immediately to the active WrappableOutputFragment by calling
setWordWrapEnabled(newState), while preserving the existing preference and
button-state updates.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 51efc43d-3698-447b-bc39-26a8859197da

📥 Commits

Reviewing files that changed from the base of the PR and between c548b56 and 4c1178b.

📒 Files selected for processing (1)
  • app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt

Comment thread app/src/main/java/com/itsaky/androidide/actions/filetree/CopyPathAction.kt Outdated
davidschachterADFA and others added 5 commits July 28, 2026 14:01
Per review feedback, use the existing ActionData.requireContext()
helper instead of data[Context::class.java]!! - same behavior (throws
if absent) but matches the idiom already used by every other action
in this package.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…-blankj-utilcode

# Conflicts:
#	editor/src/main/java/com/itsaky/androidide/editor/ui/IDEEditor.kt
Comment thread common/src/main/java/com/itsaky/androidide/utils/ContextUtils.kt
Comment thread common/src/main/java/com/itsaky/androidide/utils/FileUtils.kt
Comment thread common/src/main/java/com/itsaky/androidide/app/BaseApplication.kt Outdated
Comment thread common/src/main/java/com/itsaky/androidide/utils/ReflectUtils.kt
Comment thread common/src/main/java/com/itsaky/androidide/utils/DeviceUtils.kt Outdated
Fixes Hal Eisen's 5-point review of PR #1576:
- isSoftInputVisible() now gates the WindowInsetsCompat IME check on API 30+
  and falls back to a decor visible-frame heuristic below that, since the
  inset bit misreports on API 28-29 (MIN_SDK = 28), same as our own
  termux KeyboardUtils.isSoftKeyboardVisible() documents.
- BaseApplication drops its dead foregroundActivity tracker: IDEApplication
  is the only subclass that populates it, so the base getter now just
  returns null instead of registering an unused ActivityLifecycleCallbacks.
- ReflectUtils' four public methods now share one wrapErrors() helper
  instead of repeating the same try/catch wrapping block.
- DeviceUtils.getModel() drops a redundant .trim() before a regex that
  already strips all whitespace.
- FileUtils.kt gets a doc pointer to the pre-existing FileUtil.java,
  noting the differing failure contracts, so the next maintainer knows
  which to reach for.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@davidschachterADFA
davidschachterADFA merged commit f906a47 into stage Jul 29, 2026
4 checks passed
@davidschachterADFA
davidschachterADFA deleted the fix/ADFA-4649-remove-blankj-utilcode branch July 29, 2026 19:41
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.

3 participants