Skip to content

Fix Dracula connecting-status color; harden getColorAttr#52

Merged
hawkff merged 2 commits into
mainfrom
fix/dracula-connecting-color
Jun 21, 2026
Merged

Fix Dracula connecting-status color; harden getColorAttr#52
hawkff merged 2 commits into
mainfrom
fix/dracula-connecting-color

Conversation

@hawkff

@hawkff hawkff commented Jun 21, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the Dracula-theme discrepancy where the in-progress status text ("Connecting…",
"Testing…", "Stopping…") rendered dark/black instead of the theme's yellow — and fixes a
latent getColorAttr crash surfaced while wiring it up.

Changes

  • Add a statusConnectingColor theme attr. Default: ?attr/colorOnPrimary (no change for
    other themes). Dracula: @color/color_dracula_yellow. Use it for the Connecting state in
    StatsBar.setStatusColorByState. Previously Connecting fell into the colorOnPrimary branch,
    which resolves dark on Dracula's light-purple primary.
  • Harden getColorAttr (ktx/Utils.kt): when a theme attr resolves to a literal color
    value (resourceId == 0) rather than a @color resource reference,
    ContextCompat.getColor(0) threw Resources$NotFoundException: Resource ID #0x0. Now use the
    literal data when it is a color type, else fall back to Color.TRANSPARENT. This protects
    every getColorAttr caller, not just the new attr.

Why the crash

statusConnectingColor chained to ?attr/colorOnPrimary, which some themes define as a literal
color (no backing resource id). The old getColorAttr blindly passed resourceId (0) to
getColor, crashing on connect.

Testing

  • On-device (Dracula theme): connect/disconnect no longer crashes; the in-progress status text
    renders yellow (verified "Testing…" / connecting states). Other themes unchanged (attr defaults
    to colorOnPrimary).
  • CodeRabbit CLI quota was exhausted from iterative local runs; relying on GitHub CodeRabbit +
    Greptile for the final review.

Notes

  • The status colors already in Dracula: connected = green, stopped/stopping = red, and now
    connecting = yellow.

Greptile Summary

This PR fixes the Dracula theme showing dark/black text during connecting states and hardens getColorAttr against a crash when an attribute resolves to a literal color value rather than a color resource reference.

  • New statusConnectingColor attr: Declared in attrs.xml, defaulting to ?attr/colorOnPrimary in all themes and set to @color/color_dracula_yellow in all four Dracula variants (day/night × main/Dialog), with consistent coverage in values-v26 for API 26+.
  • getColorAttr hardening (Utils.kt): When tv.resourceId == 0 (literal value, no backing resource), the old code passed 0 to ContextCompat.getColor() causing a Resources$NotFoundException. The fix checks tv.type against TYPE_FIRST_COLOR_INT..TYPE_LAST_COLOR_INT and uses tv.data for literal colors, falling back to Color.TRANSPARENT for unexpected types.
  • StatsBar.setStatusColorByState: Connecting now routes through the new custom attr instead of the bare colorOnPrimary else branch.

Confidence Score: 5/5

Safe to merge — targeted, well-scoped fix with no regressions on non-Dracula themes and full attr coverage across all theme variants.

The getColorAttr change correctly handles the literal-color case using the documented TypedValue type-range constants, and all four Dracula theme variants (day/night × main/Dialog) as well as the API-26 override have been updated in lock-step. Non-Dracula themes inherit the new attr from the base and keep existing colorOnPrimary behaviour unchanged.

No files require special attention.

Important Files Changed

Filename Overview
app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt Hardens getColorAttr to handle literal-color attrs (resourceId==0) by checking TypedValue type range before falling back to Color.TRANSPARENT — fixes the crash root cause.
app/src/main/java/io/nekohasekai/sagernet/widget/StatsBar.kt Adds explicit Connecting state branch in setStatusColorByState, routing it through the new statusConnectingColor attr instead of the bare colorOnPrimary fallback.
app/src/main/res/values/attrs.xml Declares statusConnectingColor attr with format="color" alongside the existing Connected/Stopped attrs.
app/src/main/res/values/themes.xml Adds statusConnectingColor to the base Theme.SagerNet (defaults to ?attr/colorOnPrimary) and both Dracula variants (yellow); all other color themes inherit via parent.
app/src/main/res/values-night/themes.xml Adds statusConnectingColor=yellow to both night-mode Dracula variants (main and Dialog), which are the active code paths when the user selects Dracula.
app/src/main/res/values-v26/themes.xml Adds statusConnectingColor=?attr/colorOnPrimary to the API 26+ Theme.SagerNet override, consistent with the other status colors defined there.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[setStatusColorByState\nstate: BaseService.State] --> B{state?}
    B -->|Connected| C[R.attr.statusConnectedColor]
    B -->|Stopped / Stopping| D[R.attr.statusStoppedColor]
    B -->|Connecting NEW| E[R.attr.statusConnectingColor]
    B -->|else| F[material colorOnPrimary]

    C --> G[getColorAttr]
    D --> G
    E --> G
    F --> G

    G --> H{tv.resourceId != 0?}
    H -->|Yes| I[ContextCompat.getColor resourceId]
    H -->|No| J{tv.type in COLOR range?}
    J -->|Yes NEW| K[tv.data literal color value]
    J -->|No NEW| L[Color.TRANSPARENT safe fallback]

    I --> M[setTextColor]
    K --> M
    L --> M
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[setStatusColorByState\nstate: BaseService.State] --> B{state?}
    B -->|Connected| C[R.attr.statusConnectedColor]
    B -->|Stopped / Stopping| D[R.attr.statusStoppedColor]
    B -->|Connecting NEW| E[R.attr.statusConnectingColor]
    B -->|else| F[material colorOnPrimary]

    C --> G[getColorAttr]
    D --> G
    E --> G
    F --> G

    G --> H{tv.resourceId != 0?}
    H -->|Yes| I[ContextCompat.getColor resourceId]
    H -->|No| J{tv.type in COLOR range?}
    J -->|Yes NEW| K[tv.data literal color value]
    J -->|No NEW| L[Color.TRANSPARENT safe fallback]

    I --> M[setTextColor]
    K --> M
    L --> M
Loading

Reviews (2): Last reviewed commit: "fix(theme): add statusConnectingColor to..." | Re-trigger Greptile

…rden getColorAttr

The 'Connecting…' / 'Testing…' status text fell into the colorOnPrimary branch, which
renders dark on Dracula's light-purple primary instead of yellow. Add a
statusConnectingColor attr (default colorOnPrimary; Dracula = yellow) and use it for the
Connecting state.

While wiring it up, surfaced and fixed a latent crash in getColorAttr: when a theme attr
resolves to a literal color value (resourceId == 0) rather than a @color resource ref,
ContextCompat.getColor(0) threw Resources$NotFoundException (Resource ID #0x0). Now use
the literal data when it is a color type, falling back to TRANSPARENT otherwise. This
hardens every getColorAttr caller, not just the new attr.

Verified on-device (Dracula): connect no longer crashes; in-progress status renders yellow.
@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new statusConnectingColor theme attribute declared in attrs.xml, assigned in all four app themes (colorOnPrimary for base themes, color_dracula_yellow for Dracula themes). StatsBar.setStatusColorByState now maps the Connecting state to this new attribute. Context.getColorAttr in Utils.kt is rewritten to handle both color-resource references and literal color int values returned by theme resolution.

Changes

Connecting State Color Attribute

Layer / File(s) Summary
statusConnectingColor attribute, theme values, and StatsBar wiring
app/src/main/res/values/attrs.xml, app/src/main/res/values/themes.xml, app/src/main/java/io/nekohasekai/sagernet/widget/StatsBar.kt
Declares the statusConnectingColor color attribute, maps it to ?attr/colorOnPrimary in Theme.SagerNet and Theme.SagerNet.Dialog, to @color/color_dracula_yellow in both Dracula variants, and updates StatsBar.setStatusColorByState to use R.attr.statusConnectingColor for the Connecting state.
getColorAttr literal color resolution fix
app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt
Rewrites Context.getColorAttr to distinguish color-resource references from literal color ints via resourceId != 0, validates the resolved type against TypedValue's color-int range, and returns Color.TRANSPARENT as fallback; adds android.graphics.Color import.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • hawkff/NekoBoxForAndroid#41: Updates StatsBar status text coloring via themed color attributes and the Dracula semantic palette, directly intersecting with StatsBar.setStatusColorByState and the getColorAttr resolution path changed in this PR.

Poem

🐇 Hop hop, what color shall we show,
When the tunnel's connecting, all aglow?
A yellow for Dracula, primary for the rest,
TRANSPARENT fallback puts old bugs to rest!
The bunny resolves attrs with careful delight,
No more crashes — just colors, just right! 🎨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 two main changes: fixing Dracula's connecting-status color and hardening the getColorAttr function to prevent crashes.
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.
Description check ✅ Passed The pull request description clearly explains the bug fix for Dracula theme color rendering and the crash in getColorAttr, with detailed reasoning and testing notes that align with the changeset.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

…hemes

Per Greptile: Dracula forces night mode, so the active overrides live in
values-night/themes.xml — which had statusConnected/Stopped but not the new
statusConnectingColor, so connecting still inherited the dark base value in practice.
Add statusConnectingColor=yellow to both night Dracula blocks, and to the API26+ base
Theme.SagerNet in values-v26 (which redefines the status attrs) so the attr always
resolves. Day-mode values were added in the previous commit.
@hawkff hawkff merged commit b1c4fde into main Jun 21, 2026
5 checks passed
@hawkff hawkff deleted the fix/dracula-connecting-color branch June 21, 2026 01:40
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.

1 participant