Skip to content

fix: default speedInterval to 1000ms so fresh installs show speeds#30

Merged
hawkff merged 1 commit into
mainfrom
fix/speed-interval-default
Jun 18, 2026
Merged

fix: default speedInterval to 1000ms so fresh installs show speeds#30
hawkff merged 1 commit into
mainfrom
fix/speed-interval-default

Conversation

@hawkff

@hawkff hawkff commented Jun 18, 2026

Copy link
Copy Markdown
Owner

Summary

DataStore.speedInterval relied on stringToInt's implicit { 0 } default. On a fresh install that never opens the Settings screen, the speedInterval key is absent in the preference store, so it resolves to 0.

TrafficLooper.loop() returns early when delayMs == 0:

val delayMs = DataStore.speedInterval.toLong()
...
if (delayMs == 0L) return

This disables the traffic-stats loop entirely, so the dashboard up/down (▲▼) speeds stay permanently blank until the user opens Settings (which persists the XML defaultValue="1000").

Fix

Align the code default with global_preferences.xml (defaultValue="1000"):

var speedInterval by configurationStore.stringToInt(Key.SPEED_INTERVAL) { 1000 }

0 (Off) remains a valid explicit user choice via the settings menu — this only changes the absent-key default.

Impact

  • Fresh installs now show live up/down speeds out of the box.
  • No migration needed: users who previously set a value keep it; only the unset case changes from 0 to 1000.

Testing

  • :app:compileOssDebugKotlin passes.
  • Reproduced on-device: with the key unset the stats loop never registered (no traffic count log line); after setting the value the loop registers and ▲▼ speeds update. This patch makes 1000 the default without manual intervention.

Greptile Summary

This PR fixes a fresh-install bug where DataStore.speedInterval defaulted to 0 (the implicit stringToInt code default) because the preference key was never written until the Settings screen was opened. A 0 interval causes TrafficLooper.loop() to return immediately, leaving the dashboard ▲▼ speeds permanently blank.

  • Changes the stringToInt default lambda from { 0 } to { 1000 }, aligning it with global_preferences.xml's app:defaultValue=\"1000\".
  • Adds an explanatory comment linking the code default to its XML counterpart and to the TrafficLooper guard that triggers the regression.
  • No migration needed: users with an existing stored value are unaffected; only the absent-key path changes.

Confidence Score: 5/5

Safe to merge — the one-line change closes a well-described fresh-install gap without touching any existing stored values or runtime paths.

The fix is minimal and directly verified: Preferences.kt shows stringToInt passes the default as the getString fallback, so changing { 0 } to { 1000 } is the exact lever that controls the absent-key return value. The TrafficLooper early-return on delayMs == 0L confirms the root cause. The XML defaultValue=1000 confirms 1000 is the intended UI default. Users who previously saved a value (including 0 for Off) are unaffected because their key is present in the store.

No files require special attention.

Important Files Changed

Filename Overview
app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt Changes speedInterval default from 0 to 1000 to match global_preferences.xml, fixing the blank-speed bug on fresh installs where the preference key was never written.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant App as App (fresh install)
    participant DS as DataStore.speedInterval
    participant PS as PreferenceStore
    participant TL as TrafficLooper.loop()

    Note over App,TL: Before fix — key absent, Settings never opened
    App->>DS: read speedInterval
    DS->>PS: getString("speedInterval", "0")
    PS-->>DS: "0" (key absent, returns default "0")
    DS-->>App: 0
    App->>TL: start()
    TL->>DS: "delayMs = speedInterval.toLong() == 0L"
    TL-->>App: return (loop disabled — speeds stay blank)

    Note over App,TL: After fix — code default matches XML defaultValue=1000
    App->>DS: read speedInterval
    DS->>PS: getString("speedInterval", "1000")
    PS-->>DS: "1000" (key absent, returns default "1000")
    DS-->>App: 1000
    App->>TL: start()
    TL->>DS: "delayMs = speedInterval.toLong() == 1000L"
    TL-->>App: loop runs, speeds update every 1000ms
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"}}}%%
sequenceDiagram
    participant App as App (fresh install)
    participant DS as DataStore.speedInterval
    participant PS as PreferenceStore
    participant TL as TrafficLooper.loop()

    Note over App,TL: Before fix — key absent, Settings never opened
    App->>DS: read speedInterval
    DS->>PS: getString("speedInterval", "0")
    PS-->>DS: "0" (key absent, returns default "0")
    DS-->>App: 0
    App->>TL: start()
    TL->>DS: "delayMs = speedInterval.toLong() == 0L"
    TL-->>App: return (loop disabled — speeds stay blank)

    Note over App,TL: After fix — code default matches XML defaultValue=1000
    App->>DS: read speedInterval
    DS->>PS: getString("speedInterval", "1000")
    PS-->>DS: "1000" (key absent, returns default "1000")
    DS-->>App: 1000
    App->>TL: start()
    TL->>DS: "delayMs = speedInterval.toLong() == 1000L"
    TL-->>App: loop runs, speeds update every 1000ms
Loading

Reviews (1): Last reviewed commit: "fix: default speedInterval to 1000ms so ..." | Re-trigger Greptile

DataStore.speedInterval used stringToInt's implicit { 0 } default. On a
fresh install that never opens the Settings screen, the key is absent, so
it resolved to 0. TrafficLooper.loop() returns early when delayMs == 0,
which disables the traffic-stats loop entirely and leaves the dashboard
up/down (UP/DOWN) speeds permanently blank.

Align the code default with global_preferences.xml (defaultValue=1000) so
the stats loop runs out of the box. '0' (Off) remains a valid explicit
user choice via the settings menu.
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@hawkff, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 1 hour, 43 minutes, and 53 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

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 credits.

🚦 How do rate limits work?

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

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, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b6f903b2-cc90-4213-9f1a-35dc0e5ccd88

📥 Commits

Reviewing files that changed from the base of the PR and between bfac4af and 0ea43e5.

📒 Files selected for processing (1)
  • app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt

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

@hawkff hawkff merged commit cd76a34 into main Jun 18, 2026
5 checks passed
@hawkff hawkff deleted the fix/speed-interval-default branch June 18, 2026 17:30
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