Skip to content

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

Open
davidschachterADFA wants to merge 11 commits into
stagefrom
fix/ADFA-4649-remove-blankj-utilcode
Open

ADFA-4649: Remove com.blankj:utilcodex (AndroidUtilCode) dependency#1576
davidschachterADFA wants to merge 11 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 <[email protected]>
…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 <[email protected]>
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 <[email protected]>
…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 <[email protected]>
…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 <[email protected]>
…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 <[email protected]>
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 <[email protected]>

@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 <[email protected]>
@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

Warning

Review limit reached

@davidschachterADFA, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 20 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fdada237-2623-4359-9279-fc48484cc685

📥 Commits

Reviewing files that changed from the base of the PR and between 7242301 and 9d99c61.

📒 Files selected for processing (96)
  • 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
  • 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
✨ 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants