Conversation
Full-featured Android application for SpeedCool performance optimization: • Root and Shizuku execution modes • Real-time system monitoring (CPU, RAM, GPU, temp, battery) • Performance profiles: Eco, Balanced, Performance • Adaptive optimization engine • Thermal management and monitoring • RAM management with auto-clean • Conflict detection and resolution • Background optimization service • Material Design 3 UI with Jetpack Compose
|
Warning Review limit reached
More reviews will be available in 41 minutes and 20 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughA complete Android application implementation is added, comprising a performance optimization system with Compose UI, background monitoring service, dual-mode shell execution (root/Shizuku), and system management engines for CPU/GPU/thermal/RAM tuning. The app launches on boot, continuously monitors system status, applies optimization profiles based on thresholds, and provides a three-screen dashboard for user control. ChangesSpeedCool Android App Implementation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d4f0391f88
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| writer.write("$cmd\n") | ||
| writer.flush() | ||
| results.add(reader.readLine() ?: "") |
There was a problem hiding this comment.
Avoid blocking batch execution on missing command output
executeBatch() reads one line from stdout after every command, but most of the queued commands (for example echo ... > /sys/...) produce no stdout at all. In that case readLine() blocks waiting for a newline/EOF, so profile application and conflict resolution can hang indefinitely instead of completing.
Useful? React with 👍 / 👎.
| onStartService = { | ||
| scope.launch { | ||
| isServiceRunning = true | ||
| executorMode = engine.getExecutor()?.mode ?: "none" | ||
| } | ||
| }, | ||
| onStopService = { | ||
| isServiceRunning = false |
There was a problem hiding this comment.
Start and stop the real service from dashboard actions
The dashboard's start/stop handlers only flip local Compose state and never call startForegroundService()/stopService(), so tapping “Iniciar/Parar” does not actually control SpeedCoolService. Users can believe optimization is running while no background service exists once the UI leaves composition.
Useful? React with 👍 / 👎.
| autoStart = false, | ||
| backgroundService = true, | ||
| autoRamClean = true, | ||
| learningEnabled = true, | ||
| conflictAutoResolve = true, | ||
| tempUnit = "celsius", | ||
| ramCleanInterval = 180, | ||
| onAutoStartChange = {}, | ||
| onBackgroundServiceChange = {}, | ||
| onAutoRamCleanChange = {}, | ||
| onLearningEnabledChange = {}, | ||
| onConflictAutoResolveChange = {}, | ||
| onTempUnitChange = {}, | ||
| onRamCleanIntervalChange = {} |
There was a problem hiding this comment.
Bind settings screen to persisted state instead of constants
The settings route passes hard-coded values and no-op callbacks into SettingsScreen, so switches/sliders cannot persist or even reflect user changes. This makes core controls like auto-start/background service effectively non-functional despite the presence of a SettingsData store.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 20
🧹 Nitpick comments (5)
.github/workflows/android-app.yml (1)
18-19: ⚡ Quick winAvoid suppressing errors in SDK setup commands.
The
|| trueon line 18 and output redirection on line 19 swallow all errors, making build failures difficult to diagnose. Ifsdkmanagerfails to install components, the subsequent gradle build will fail with confusing errors.Additionally, when running
sdkmanagerfrom a non-standard location, explicitly passing--sdk_root=$ANDROID_HOMEensures it locates the SDK correctly.🔧 Proposed fix to improve error visibility and SDK setup robustness
- yes | sdkmanager --licenses > /dev/null 2>&1 || true - sdkmanager "platforms;android-34" "build-tools;34.0.0" > /dev/null 2>&1 + yes | sdkmanager --sdk_root=$ANDROID_HOME --licenses || true + sdkmanager --sdk_root=$ANDROID_HOME "platforms;android-34" "build-tools;34.0.0"🤖 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 @.github/workflows/android-app.yml around lines 18 - 19, Remove the error-suppression and output-redirection around the sdkmanager calls so failures are visible: delete the "|| true" and the "> /dev/null 2>&1" redirections on the lines invoking sdkmanager, and instead let the command return a non-zero exit code (or explicitly check its exit status and exit 1 on failure). Also make the commands robust by adding the SDK root flag (use --sdk_root="$ANDROID_HOME") to both the license acceptance invocation (e.g., sdkmanager --licenses) and the package install invocation (sdkmanager "platforms;android-34" "build-tools;34.0.0"), so the workflow fails loudly if sdkmanager cannot find or install components.gradlew.bat (1)
17-92: ⚡ Quick winConsider using the official Gradle wrapper script.
Like
gradlew, this appears to be a simplified custom wrapper. The official Gradle-generatedgradlew.batincludes additional error handling, proper path resolution, and has been battle-tested across diverse Windows environments.Generate the official wrapper by running:
gradle wrapper --gradle-version 8.5🤖 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 `@gradlew.bat` around lines 17 - 92, The current gradlew.bat is a simplified custom wrapper missing the official Gradle wrapper's robust error handling and path resolution; replace this file with the official Gradle-generated script by running "gradle wrapper --gradle-version 8.5" in the project root and committing the generated gradlew.bat (and associated gradle/wrapper files), ensuring the new script preserves proper handling around symbols/labels shown in the diff such as DEFAULT_JVM_OPTS, findJavaFromJavaHome, :execute, :fail and the CLASSPATH/GradleWrapperMain invocation so you get the battle-tested behavior for Windows environments..gradle/buildOutputCleanup/cache.properties (1)
1-2: 💤 Low valueConsider excluding Gradle build cache from version control.
The
.gradle/directory contains build cache and is typically excluded via.gitignore. While including it is harmless, it adds unnecessary files to the repository.Add to
.gitignore:.gradle/🤖 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 @.gradle/buildOutputCleanup/cache.properties around lines 1 - 2, The repository currently contains the generated Gradle cache file cache.properties under the .gradle directory which should be excluded from VCS; update the project's .gitignore to add the entry ".gradle/" and then remove the tracked .gradle files from the index (e.g., git rm -r --cached .gradle) and commit the change so the cached build files (cache.properties) are no longer versioned.gradle/wrapper/gradle-wrapper.properties (1)
3-3: Validate Gradle wrapper version (compatibility + security)Gradle 8.5 is compatible with Android Gradle Plugin 8.2.2 (AGP 8.2 requires minimum Gradle 8.2). The Gradle-core security advisories found are patched in Gradle 6.0 and 5.4.0, so they don’t impact Gradle 8.5. Latest Gradle (as of 2026-05-26) is 9.5.1—upgrade only if your AGP/project constraints allow.
🤖 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 `@gradle/wrapper/gradle-wrapper.properties` at line 3, The Gradle wrapper distributionUrl currently pins Gradle 8.5; validate compatibility with your Android Gradle Plugin (AGP) and project constraints and then either keep 8.5 or upgrade the distributionUrl to a vetted newer Gradle (e.g., 9.5.1) if AGP/project allow; update the distributionUrl entry accordingly and add a short comment or upgrade note in your repo describing the AGP version constraint so future reviewers know why you chose 8.5 versus a newer Gradle.app/src/main/java/com/speedcool/app/ui/screens/StatusScreen.kt (1)
135-140: ⚡ Quick winRename function to match its actual purpose.
The function is named
executorModeDisplaybut it mapsProfileenum values, notExecutorMode. Based on the review context,ExecutorModeis a separate domain model (likely representing Root vs. Shizuku execution modes).Rename to
profileDisplayorprofileDisplayNamefor clarity.♻️ Proposed refactor
-private fun executorModeDisplay(profile: Profile): String = when (profile) { +private fun profileDisplay(profile: Profile): String = when (profile) { Profile.ECO -> "Eco" Profile.BALANCED -> "Equilibrado" Profile.PERFORMANCE -> "Performance" Profile.LEARNING -> "Aprendizado" }And update the call site on line 103:
- StatusItem("Modo", executorModeDisplay(status.activeProfile)) + StatusItem("Modo", profileDisplay(status.activeProfile))🤖 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/speedcool/app/ui/screens/StatusScreen.kt` around lines 135 - 140, Rename the function executorModeDisplay to profileDisplayName (or profileDisplay) because it maps Profile enum values, not ExecutorMode; update the function declaration private fun executorModeDisplay(profile: Profile): String to the new name (e.g., private fun profileDisplayName(profile: Profile): String) and change all call sites that invoke executorModeDisplay(...) (for example the call at the previously noted call site around line 103) to use the new name so callers compile cleanly; leave the when mapping of Profile.ECO, Profile.BALANCED, Profile.PERFORMANCE, Profile.LEARNING unchanged.
🤖 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 @.github/workflows/android-app.yml:
- Around line 7-11: Pin the reusable GitHub Actions and disable credential
persistence: replace mutable tags like actions/checkout@v4 and
actions/setup-java@v4 with their pinned commit SHAs (use the specific commit SHA
for actions/checkout and actions/setup-java) and add persist-credentials: false
under the actions/checkout step to prevent leaking git auth; also pin any other
actions used (e.g., actions/upload-artifact) to their SHAs to eliminate
supply-chain risk.
In `@app/proguard-rules.pro`:
- Line 2: The proguard rule "-keep class com.speedcool.app.** { *; }" is too
broad and disables R8 obfuscation; replace it by removing the blanket keep and
instead add narrow keep rules only for types/members that are reflectively
accessed or required by frameworks (e.g., classes referenced via Class.forName,
Gson/Jackson model classes, Room entities/DAOs, Parcelable implementations,
Android components like Activities/Services/BroadcastReceivers). Audit usages of
reflection/serialization and add targeted rules such as -keep,
-keepclassmembers, or -keepnames for those specific fully-qualified classes
(referencing the com.speedcool.app package names of the affected classes) rather
than keeping com.speedcool.app.** wholesale. Ensure Android components remain
kept/instantiable and any serialization-mapped model fields used by Gson/Jackson
are preserved.
In `@app/src/main/AndroidManifest.xml`:
- Line 18: Replace the hard-coded app label "SpeedCool" in the Android manifest
with a string resource reference: update the android:label attribute (in
AndroidManifest.xml) to use `@string/app_name` and ensure the app_name entry
exists in res/values/strings.xml; if missing, add app_name="SpeedCool" (or
localized values) to strings.xml so the manifest references a resource rather
than a literal.
- Line 9: Remove the unnecessary uses-permission entry for
android.permission.REQUEST_INSTALL_PACKAGES from the manifest (no
PackageInstaller/ACTION_INSTALL_PACKAGE usage in the codebase); also replace the
hardcoded android:label="SpeedCool" attribute with the string resource reference
android:label="`@string/app_name`" so the app uses the localized value from
res/values/strings.xml.
In `@app/src/main/java/com/speedcool/app/data/SettingsData.kt`:
- Around line 54-66: The settingsFlow currently maps context.dataStore.data
directly and will crash on DataStore corruption; wrap the upstream flow with a
catch that converts CorruptionException into a safe fallback (e.g.,
emit(emptyPreferences())) before the map so the collector won't fail.
Concretely, update settingsFlow to use context.dataStore.data.catch { e -> if (e
is CorruptionException) { /* log */ emit(emptyPreferences()) } else throw e } .
Then keep the existing .map { prefs -> Settings(...) } unchanged; reference
symbols: settingsFlow, context.dataStore.data, CorruptionException,
emptyPreferences().
In `@app/src/main/java/com/speedcool/app/engine/OptimizationEngine.kt`:
- Line 70: The summary flag conflictsDetected in OptimizationEngine.kt currently
checks only conflictingModules and suspiciousGovernors; update its condition to
also consider disabled thermal zones by including
conflicts.thermalZonesOff.isNotEmpty() (or the appropriate thermalZonesOff
predicate) in the OR expression so any disabled thermal zone will set
conflictsDetected to true; modify the assignment that sets conflictsDetected and
ensure it still guards against conflicts being null.
- Around line 13-16: SystemStatus.conflictsDetected currently omits
ConflictInfo.thermalZonesOff and the manager fields (perfMan, thermMan, ramMan,
conflictDetector) are lazy based on nullable executor causing them to remain
null if called before initialize(); modify conflictsDetected to include
thermalZonesOff in its combined check, and change manager lifecycle so they are
created during initialize() (using the non-null executor) and explicitly nulled
in cleanup(); also update checkConflicts(), resolveConflicts(), and cleanRam()
to guard against uninitialized managers by either requiring initialize()
(throw/early-return) or checking for null and no-op, ensuring managers are not
permanently locked as null.
In `@app/src/main/java/com/speedcool/app/engine/PerformanceManager.kt`:
- Around line 20-22: The shell arithmetic currently references an undefined
shell variable `percent`, causing incorrect limits; update the cmds.add call
that builds the arithmetic string in PerformanceManager (where the two cmds.add
lines are created) to inject the Kotlin `percent` argument via string
interpolation (e.g., use ${percent} inside the Kotlin string) so the resulting
shell command becomes echo $((val * <actual_percent> / 100)) and continues to
escape shell variables like $val and $cpu as before.
- Around line 41-65: applyProfile currently ignores failures by only using
onSuccess and then treating at-least-one-success as overall success; change it
to fail-fast: for each call to setCpuGovernor, setCpuFreqLimit, setIOScheduler,
and optimizeGpu inside applyProfile, capture the Result return, if it's failure
immediately return Result.failure(result.exceptionOrNull() ?:
RuntimeException("...")), otherwise append the success value and continue; do
this for every branch (Profile.ECO, Profile.PERFORMANCE, default) so any failing
step short-circuits and returns the underlying error instead of masking partial
application.
In `@app/src/main/java/com/speedcool/app/root/ShellExecutor.kt`:
- Around line 31-46: The batch executor blocks on reader.readLine() for
stdout-silent commands in executeBatch; change RootExecutor.executeBatch and
ShizukuExecutor.executeBatch so you do not call reader.readLine() once per
command synchronously—write all commands (or write and flush each command) then
close the writer (or send "exit\n") and drain the process streams until EOF
instead of blocking per-command: after sending commands, read from
process.inputStream and process.errorStream fully (e.g., loop reading lines
until null or until process.waitFor() completes) to collect stdout/stderr for
the whole batch, capture stderr content into results or a separate error list,
call process.waitFor() and check process.exitValue() and return Result.failure
when non-zero, and ensure reader/writer/process are closed to avoid leaks;
reference executeBatch, reader.readLine, writer, process.waitFor, and
process.exitValue when locating the code to change.
In `@app/src/main/java/com/speedcool/app/service/SpeedCoolService.kt`:
- Around line 35-53: The periodic optimization loop inside the scope.launch in
SpeedCoolService.kt can be killed by any exception from engine?.collectStatus(),
applyProfile(), or cleanRam(); wrap the per-iteration logic (the block that
calls engine.initialize(...), collectStatus(), applyProfile(...), and
cleanRam()) in a try-catch that catches Throwable, logs the error (including the
exception details) via your logger, and continues to the next delay so one
failure doesn't cancel the coroutine; ensure the try-catch surrounds each
iteration (not the whole coroutine startup) and still respects the existing
delay(30000) behavior.
In `@app/src/main/java/com/speedcool/app/SpeedCoolApp.kt`:
- Around line 24-28: The notification channel name/description are hardcoded in
the channel creation (the NotificationChannel instantiation in SpeedCoolApp.kt);
replace the literal strings with resource lookups by adding two string resources
(e.g., notification_channel_name and notification_channel_description) in
res/values/strings.xml and use
context.getString(R.string.notification_channel_name) and
context.getString(R.string.notification_channel_description) when constructing
the NotificationChannel so the user-visible text is localized and centrally
managed.
In `@app/src/main/java/com/speedcool/app/ui/components/Common.kt`:
- Around line 153-154: Clamp the usagePercent to the 0–100 range before
computing the bar width so out-of-range values can't produce invalid sizes:
replace the direct use of usagePercent in the Size constructor with a clamped
value (e.g., val clamped = usagePercent.coerceIn(0f, 100f)) and use clamped in
Size(size.width * (clamped / 100f), size.height); keep the CornerRadius line
unchanged.
- Around line 42-43: StatCard is currently referencing the composable lambda
icon without invoking it—change the reference to call icon() where the icon
should be emitted (look for the icon: `@Composable` () -> Unit parameter and the
place that currently just mentions icon). In RamBar, clamp the usagePercent
input to the 0..100 range before computing widths (use something like
usagePercent.coerceIn(0f, 100f) and then compute the fraction or dp width from
that clamped value) so bar sizing cannot be driven outside expected bounds;
update any calculations that derive filledWidth from usagePercent to use the
clamped value.
In `@app/src/main/java/com/speedcool/app/ui/screens/StatusScreen.kt`:
- Line 103: The UI label "Modo" is misleading because StatusItem("Modo",
executorModeDisplay(status.activeProfile)) actually shows the performance
profile (status.activeProfile) not the executor mode; update the label to
"Perfil" to match the data, and optionally rename the helper to something
clearer (e.g., profileDisplay) or create a new function for executor mode
display — locate the StatusItem call and change the first argument from "Modo"
to "Perfil" and adjust or rename executorModeDisplay(status.activeProfile) to a
clearer identifier (or implement a separate executorModeDisplay function if you
intend to show executor mode instead).
- Line 62: Status UI shows GPU frequency because StatusScreen uses
status.gpuFreq; add a cpuFreq field to the SystemStatus data model and populate
it in OptimizationEngine.collectStatus() by reading CPU scaling_cur_freq (e.g.
/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq), convert from kHz to MHz,
then update StatusScreen to display status.cpuFreq in the "Frequência"
StatusItem instead of status.gpuFreq. Ensure null/error handling when reading
sysfs and that the value is formatted as an integer/decimal MHz string for the
UI.
In `@app/src/main/java/com/speedcool/app/ui/SpeedCoolNavHost.kt`:
- Around line 107-122: The SettingsScreen call in SpeedCoolNavHost.kt currently
passes hardcoded state and empty callbacks (autoStart, backgroundService,
autoRamClean, learningEnabled, conflictAutoResolve, tempUnit, ramCleanInterval
and
onAutoStartChange/onBackgroundServiceChange/onAutoRamCleanChange/onLearningEnabledChange/onConflictAutoResolveChange/onTempUnitChange/onRamCleanIntervalChange),
so user interactions are no-ops; instead, wire these props to real state and
handlers: obtain or inject a SettingsViewModel (or persistent settings
repository) in SpeedCoolNavHost, replace the hardcoded booleans/values with
ViewModel-backed LiveData/StateFlow values, and implement each callback to call
appropriate ViewModel methods (e.g., settingsViewModel.setAutoStart(enabled),
setBackgroundService(...), setRamCleanInterval(...), setTempUnit(...)) so
changes persist/update UI; ensure SettingsScreen receives current state from the
ViewModel rather than literal literals and that callbacks dispatch updates to
the store.
- Around line 83-91: onStartService/onStopService currently only flip the UI
flag (isServiceRunning) and never start/stop the Android service; update the
handlers in SpeedCoolNavHost so onStartService constructs the service Intent
(targeting your foreground service class), calls
Context.startForegroundService(intent) (or ContextCompat.startForegroundService
if needed), then set isServiceRunning = true and executorMode as done now, and
have onStopService call context.stopService(intent) and set isServiceRunning =
false; locate the anonymous handlers named onStartService and onStopService (and
the usage of scope.launch, isServiceRunning, executorMode, engine.getExecutor())
and inject the startForegroundService/stopService calls using the
Activity/Compose Context available in this scope, handling any returned errors
by logging and only updating UI state after attempting to start/stop the
service.
In `@gradlew`:
- Around line 1-12: The current gradlew script is a minimal custom wrapper (uses
variables like JAVACMD, DIR and directly invokes
org.gradle.wrapper.GradleWrapperMain) and should be replaced with the official
Gradle-generated wrapper that includes Darwin/Cygwin detection, robust arg
parsing and error handling; regenerate and commit the proper wrapper by running
the Gradle wrapper task (e.g., run gradle wrapper --gradle-version 8.5) which
will produce the full gradlew script, gradle/wrapper/gradle-wrapper.jar and
gradle/wrapper/gradle-wrapper.properties to replace the current custom script.
In `@gradlew.bat`:
- Line 1: The gradlew.bat file currently has Unix (LF) line endings which will
break Windows batch parsing; update gradlew.bat to use CRLF line endings so
Windows cmd handles GOTO/CALL labels correctly. Fix by converting the file to
CRLF (e.g., run a unix2dos conversion or change the file's EOL in your editor)
and/or add a .gitattributes rule (e.g., set gradlew.bat to text eol=crlf) or
enable core.autocrlf so future commits preserve CRLF; commit the updated
gradlew.bat with CRLF line endings.
---
Nitpick comments:
In @.github/workflows/android-app.yml:
- Around line 18-19: Remove the error-suppression and output-redirection around
the sdkmanager calls so failures are visible: delete the "|| true" and the ">
/dev/null 2>&1" redirections on the lines invoking sdkmanager, and instead let
the command return a non-zero exit code (or explicitly check its exit status and
exit 1 on failure). Also make the commands robust by adding the SDK root flag
(use --sdk_root="$ANDROID_HOME") to both the license acceptance invocation
(e.g., sdkmanager --licenses) and the package install invocation (sdkmanager
"platforms;android-34" "build-tools;34.0.0"), so the workflow fails loudly if
sdkmanager cannot find or install components.
In @.gradle/buildOutputCleanup/cache.properties:
- Around line 1-2: The repository currently contains the generated Gradle cache
file cache.properties under the .gradle directory which should be excluded from
VCS; update the project's .gitignore to add the entry ".gradle/" and then remove
the tracked .gradle files from the index (e.g., git rm -r --cached .gradle) and
commit the change so the cached build files (cache.properties) are no longer
versioned.
In `@app/src/main/java/com/speedcool/app/ui/screens/StatusScreen.kt`:
- Around line 135-140: Rename the function executorModeDisplay to
profileDisplayName (or profileDisplay) because it maps Profile enum values, not
ExecutorMode; update the function declaration private fun
executorModeDisplay(profile: Profile): String to the new name (e.g., private fun
profileDisplayName(profile: Profile): String) and change all call sites that
invoke executorModeDisplay(...) (for example the call at the previously noted
call site around line 103) to use the new name so callers compile cleanly; leave
the when mapping of Profile.ECO, Profile.BALANCED, Profile.PERFORMANCE,
Profile.LEARNING unchanged.
In `@gradle/wrapper/gradle-wrapper.properties`:
- Line 3: The Gradle wrapper distributionUrl currently pins Gradle 8.5; validate
compatibility with your Android Gradle Plugin (AGP) and project constraints and
then either keep 8.5 or upgrade the distributionUrl to a vetted newer Gradle
(e.g., 9.5.1) if AGP/project allow; update the distributionUrl entry accordingly
and add a short comment or upgrade note in your repo describing the AGP version
constraint so future reviewers know why you chose 8.5 versus a newer Gradle.
In `@gradlew.bat`:
- Around line 17-92: The current gradlew.bat is a simplified custom wrapper
missing the official Gradle wrapper's robust error handling and path resolution;
replace this file with the official Gradle-generated script by running "gradle
wrapper --gradle-version 8.5" in the project root and committing the generated
gradlew.bat (and associated gradle/wrapper files), ensuring the new script
preserves proper handling around symbols/labels shown in the diff such as
DEFAULT_JVM_OPTS, findJavaFromJavaHome, :execute, :fail and the
CLASSPATH/GradleWrapperMain invocation so you get the battle-tested behavior for
Windows environments.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7b672db2-1517-40f1-875d-d47b0f0d522e
⛔ Files ignored due to path filters (8)
.gradle/8.5/checksums/checksums.lockis excluded by!**/*.lock.gradle/8.5/checksums/md5-checksums.binis excluded by!**/*.bin.gradle/8.5/checksums/sha1-checksums.binis excluded by!**/*.bin.gradle/8.5/dependencies-accessors/dependencies-accessors.lockis excluded by!**/*.lock.gradle/8.5/fileHashes/fileHashes.lockis excluded by!**/*.lock.gradle/buildOutputCleanup/buildOutputCleanup.lockis excluded by!**/*.lock.gradle/buildOutputCleanup/outputFiles.binis excluded by!**/*.bingradle/wrapper/gradle-wrapper.jaris excluded by!**/*.jar
📒 Files selected for processing (34)
.github/workflows/android-app.yml.gradle/buildOutputCleanup/cache.propertiesapp/build.gradle.ktsapp/proguard-rules.proapp/src/main/AndroidManifest.xmlapp/src/main/java/com/speedcool/app/MainActivity.ktapp/src/main/java/com/speedcool/app/SpeedCoolApp.ktapp/src/main/java/com/speedcool/app/data/Models.ktapp/src/main/java/com/speedcool/app/data/SettingsData.ktapp/src/main/java/com/speedcool/app/engine/ConflictDetector.ktapp/src/main/java/com/speedcool/app/engine/OptimizationEngine.ktapp/src/main/java/com/speedcool/app/engine/PerformanceManager.ktapp/src/main/java/com/speedcool/app/engine/RAMManager.ktapp/src/main/java/com/speedcool/app/engine/ThermalManager.ktapp/src/main/java/com/speedcool/app/root/ShellExecutor.ktapp/src/main/java/com/speedcool/app/service/BootReceiver.ktapp/src/main/java/com/speedcool/app/service/SpeedCoolService.ktapp/src/main/java/com/speedcool/app/ui/SpeedCoolNavHost.ktapp/src/main/java/com/speedcool/app/ui/components/Common.ktapp/src/main/java/com/speedcool/app/ui/screens/DashboardScreen.ktapp/src/main/java/com/speedcool/app/ui/screens/SettingsScreen.ktapp/src/main/java/com/speedcool/app/ui/screens/StatusScreen.ktapp/src/main/java/com/speedcool/app/ui/theme/Color.ktapp/src/main/java/com/speedcool/app/ui/theme/Theme.ktapp/src/main/res/drawable/ic_launcher_foreground.xmlapp/src/main/res/values/colors.xmlapp/src/main/res/values/strings.xmlapp/src/main/res/values/themes.xmlbuild.gradle.ktsgradle.propertiesgradle/wrapper/gradle-wrapper.propertiesgradlewgradlew.batsettings.gradle.kts
| - uses: actions/checkout@v4 | ||
| - uses: actions/setup-java@v4 | ||
| with: | ||
| java-version: '17' | ||
| distribution: 'temurin' |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
What are the latest commit SHAs for actions/checkout v4, actions/setup-java v4, and actions/upload-artifact v4?
💡 Result:
As of May 26, 2026, GitHub Actions tags for version 4 (e.g., v4, v4.1.6, v4.7.1, v7.0.1) are updated over time by the maintainers and point to specific, evolving commits. GitHub Actions are mutable by design when referenced by tags, meaning the commit SHA associated with a version like v4 can change as maintainers push updates to that branch [1]. For security and reproducibility, it is strongly recommended to pin actions to their immutable commit SHAs rather than tags [1][2]. Because the commit SHAs for versioned tags (like v4) are not static and are subject to change by the repository maintainers, they cannot be provided as a single, permanently accurate value [1]. To find the current SHA associated with a specific version tag in your workflow: 1. Navigate to the official repository (e.g., github.com/actions/checkout). 2. Click on the Tags section or the branch/tag dropdown menu. 3. Select the desired tag (e.g., v4). 4. The repository view will update to that tag, and you can see the latest commit SHA associated with it in the file browser or by viewing the commit history [3][4]. For example, referencing actions/checkout@v4 in your workflow will pull the code from whatever commit the maintainers have currently set as the target for that tag [1]. To lock your workflow to a specific, immutable version, you should replace the tag with the full commit SHA, such as: - uses: actions/checkout@ Always consult the official repository release pages for the most recent updates and security information [5][6][7].
Citations:
- 1: https://gbhackers.com/compromised-github-action/
- 2: https://blog.trailofbits.com/2026/05/22/we-hardened-zizmors-github-actions-static-analyzer/
- 3: https://github.com/actions/checkout/tree/releases/v4.0.0
- 4: https://github.com/actions/upload-artifact/tree/v4
- 5: https://github.com/actions/checkout
- 6: https://github.com/actions/setup-java
- 7: https://github.com/actions/upload-artifact
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== File: .github/workflows/android-app.yml =="
sed -n '1,120p' .github/workflows/android-app.yml | nl -baRepository: Llucs/SpeedCool-Magisk-Module
Length of output: 180
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== File: .github/workflows/android-app.yml =="
cat -n .github/workflows/android-app.yml | sed -n '1,200p'Repository: Llucs/SpeedCool-Magisk-Module
Length of output: 1215
🌐 Web query:
actions/checkout v4 persist-credentials default value
💡 Result:
The default value for the persist-credentials input in actions/checkout v4 is true [1][2]. When this setting is enabled (the default), the action configures the local git repository with the authentication token used for the checkout [1][3]. This allows subsequent steps in your workflow to perform authenticated git commands [1][3]. The token is stored in the local git configuration and is removed during the post-job cleanup phase [1][3]. Because the credentials remain available to the job while it is running, it is a recommended security practice to set persist-credentials to false if your workflow does not require authenticated git operations in subsequent steps [4]. This prevents the credential from being exposed to other steps or potentially leaked if the runner environment or artifacts are compromised [5][4]. To disable this behavior, you can explicitly set the input in your workflow YAML: - uses: actions/checkout@v4 with: persist-credentials: false
Citations:
- 1: https://github.com/actions/checkout/tree/v4
- 2: https://github.com/actions/checkout
- 3: https://github.com/actions/checkout/blob/v4/README.md
- 4: https://yossarian.net/til/post/actions-checkout-can-leak-github-credentials/
- 5: Remove
persist-credentialsor change the default tofalseactions/checkout#485
🌐 Web query:
actions/checkout v4.2.2 commit 11bd71901bbe5b1630ceea73d27597364c9af683
💡 Result:
actions/checkout v4.2.2 corresponds to commit 11bd71901bbe5b1630ceea73d27597364c9af683. This commit is part of the v4.2.2 release and includes changes noted in the v4.2.2 changelog: url-helper.ts leveraging well-known environment variables and expanded unit test coverage for isGhes [1][2]. The v4.2.2 release page for actions/checkout was published on 2024-10-23 and links to the v4.2.1...v4.2.2 full changelog comparison [2].
Citations:
- 1: actions/checkout@d632683...11bd719
- 2: https://github.com/actions/checkout/releases/tag/v4.2.2
🌐 Web query:
actions/setup-java v4.7.1 commit c5195efecf7bdfc987ee8bae7a71cb8b11521c00
💡 Result:
The GitHub Actions actions/setup-java version v4.7.1 is associated with commit c5195efecf7bdfc987ee8bae7a71cb8b11521c00 [1][2]. This version was released on April 9, 2025 [1]. The update primarily focused on documentation improvements and dependency upgrades, including [1][2]: * Documentation: Added recommendations for using GraalVM JDK 17 version 17.0.12 to align with GFTC license terms and removed duplicated GraalVM documentation [1]. * Dependency Updates: Upgraded @actions/cache to 4.0.3, @actions/glob to 0.5.0, and ts-jest to 29.2.5 [1][2].
Citations:
- 1: https://github.com/actions/setup-java/releases/tag/v4.7.1
- 2: https://dart.googlesource.com/co19/+/ccda634a15d62b23b427ae260991bd7580164661%5E%21/
🌐 Web query:
actions/upload-artifact v4.6.2 commit ea165f8d65b6e75b540449e92b4886f43607fa02
💡 Result:
The commit ea165f8d65b6e75b540449e92b4886f43607fa02 corresponds to the version v4.6.2 of the actions/upload-artifact GitHub Action [1][2][3]. Released on March 19, 2025, v4.6.2 included an update to use the artifact 2.3.2 package [1]. As of early 2026, this version is recognized as running on Node.js 20 [4]. Because GitHub Actions migrated support to Node.js 24, workflows using this specific version of the action have triggered deprecation warnings [4][5], as the action platform transitioned away from Node.js 20 in favor of Node.js 24 [5]. Users are advised to upgrade to newer versions (such as v6 or later) which provide native Node.js 24 support [5].
Citations:
- 1: https://github.com/actions/upload-artifact/releases/tag/v4.6.2
- 2: actions/upload-artifact@ea165f8...b7c566a
- 3: https://gitea.psi.ch/actions/upload-artifact/src/commit/ea165f8d65b6e75b540449e92b4886f43607fa02/CONTRIBUTING.md
- 4: Node.js 20 actions are deprecated (
actions/upload-artifact@v4.6.2) actions/upload-pages-artifact#138 - 5: https://github.com/actions/upload-artifact/releases
Pin GitHub Actions to commit SHAs and disable checkout credential persistence (lines 7-8, 22)
Unpinned @v4 tags are mutable (supply-chain risk). Also, actions/checkout@v4 defaults persist-credentials to true, so set persist-credentials: false if subsequent steps don’t need git auth.
🔒 Proposed fix to pin actions and disable credential persistence
- - uses: actions/checkout@v4
- - uses: actions/setup-java@v4
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ persist-credentials: false
+ - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
with:
java-version: '17'
distribution: 'temurin'And for upload-artifact:
- - uses: actions/upload-artifact@v4
+ - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-java@v4 | |
| with: | |
| java-version: '17' | |
| distribution: 'temurin' | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 | |
| with: | |
| java-version: '17' | |
| distribution: 'temurin' |
🧰 Tools
🪛 zizmor (1.25.2)
[warning] 7-7: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 7-7: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 8-8: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 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 @.github/workflows/android-app.yml around lines 7 - 11, Pin the reusable
GitHub Actions and disable credential persistence: replace mutable tags like
actions/checkout@v4 and actions/setup-java@v4 with their pinned commit SHAs (use
the specific commit SHA for actions/checkout and actions/setup-java) and add
persist-credentials: false under the actions/checkout step to prevent leaking
git auth; also pin any other actions used (e.g., actions/upload-artifact) to
their SHAs to eliminate supply-chain risk.
| <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" /> | ||
| <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> | ||
| <uses-permission android:name="android.permission.WAKE_LOCK" /> | ||
| <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether the app really performs package installation.
# Expected: if no relevant matches are found, remove Line 9 permission.
rg -n -C2 --type=kt 'PackageInstaller|ACTION_INSTALL_PACKAGE|application/vnd\.android\.package-archive|Intent\.ACTION_VIEW|SessionParams|REQUEST_INSTALL_PACKAGES'
rg -n -C2 --type=xml 'REQUEST_INSTALL_PACKAGES|ACTION_INSTALL_PACKAGE|package-archive'Repository: Llucs/SpeedCool-Magisk-Module
Length of output: 683
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for install-related APIs/intents/URI types that would justify REQUEST_INSTALL_PACKAGES
rg -n --hidden --no-ignore-vcs \
'PackageInstaller|ACTION_INSTALL_PACKAGE|ACTION_VIEW|package-archive|application/vnd\.android\.package-archive|SessionParams|REQUEST_INSTALL_PACKAGES' .
# Narrow to code-level install intents (commonly for sideload/install flows)
rg -n --hidden --no-ignore-vcs \
'Intent\.ACTION_(VIEW|INSTALL)|putExtra\(|setDataAndType\(|application/vnd\.android\.package-archive' .
# Also check for any mention of "install packages" user flows / UI strings
rg -n --hidden --no-ignore-vcs \
'install (package|apk|application)|sideload' .Repository: Llucs/SpeedCool-Magisk-Module
Length of output: 193
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check for hardcoded label(s) in the manifest
rg -n --hidden --no-ignore-vcs 'android:label\s*=' app/src/main/AndroidManifest.xml
# Broader search for any install/sideload UI/actions that might not match earlier keyword set
rg -n --hidden --no-ignore-vcs \
'install (package|packages)|sideload|APK|package-archive|REQUEST_INSTALL_PACKAGES|PackageInstaller|ACTION_INSTALL_PACKAGE|ACTION_VIEW.*(apk|package|archive)' \
app/src/main . || trueRepository: Llucs/SpeedCool-Magisk-Module
Length of output: 348
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n --hidden --no-ignore-vcs \
'REQUEST_INSTALL_PACKAGES|android\.permission\.REQUEST_INSTALL_PACKAGES|PackageInstaller|ACTION_INSTALL_PACKAGE|ACTION_VIEW|package-archive|application/vnd\.android\.package-archive|SessionParams' . || true
rg -n --hidden --no-ignore-vcs \
'startActivity.*(apk|package-archive|install)|setDataAndType\(|FLAG_GRANT_READ_URI_PERMISSION|android\.net\.Uri|content://.*\.apk|FileProvider.*\.apk' . || trueRepository: Llucs/SpeedCool-Magisk-Module
Length of output: 193
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find any string resource for app name/SpeedCool
rg -n --hidden --no-ignore-vcs 'SpeedCool' app/src/main/res || true
rg -n --hidden --no-ignore-vcs 'name\s*=\s*".*SpeedCool|app_name' app/src/main/res/values || true
# See if manifest label uses a `@string` reference anywhere else
rg -n --hidden --no-ignore-vcs 'android:label\s*=\s*"`@string/`' app/src/main/AndroidManifest.xml || trueRepository: Llucs/SpeedCool-Magisk-Module
Length of output: 725
Remove REQUEST_INSTALL_PACKAGES unless the app truly installs APKs
android.permission.REQUEST_INSTALL_PACKAGESis present only inapp/src/main/AndroidManifest.xml, with no repo references to install flows (PackageInstaller,ACTION_INSTALL_PACKAGE,package-archive, or related install intents).app/src/main/AndroidManifest.xmlhardcodesandroid:label="SpeedCool"even thoughapp/src/main/res/values/strings.xmlprovides@string/app_name; use the string resource for localization.
🤖 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/AndroidManifest.xml` at line 9, Remove the unnecessary
uses-permission entry for android.permission.REQUEST_INSTALL_PACKAGES from the
manifest (no PackageInstaller/ACTION_INSTALL_PACKAGE usage in the codebase);
also replace the hardcoded android:label="SpeedCool" attribute with the string
resource reference android:label="`@string/app_name`" so the app uses the
localized value from res/values/strings.xml.
| android:allowBackup="true" | ||
| android:icon="@drawable/ic_launcher_foreground" | ||
| android:roundIcon="@drawable/ic_launcher_foreground" | ||
| android:label="SpeedCool" |
There was a problem hiding this comment.
Use a string resource for the app label.
android:label="SpeedCool" should reference @string/app_name to support localization and consistent branding.
💡 Suggested change
- android:label="SpeedCool"
+ android:label="`@string/app_name`"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| android:label="SpeedCool" | |
| android:label="`@string/app_name`" |
🤖 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/AndroidManifest.xml` at line 18, Replace the hard-coded app
label "SpeedCool" in the Android manifest with a string resource reference:
update the android:label attribute (in AndroidManifest.xml) to use
`@string/app_name` and ensure the app_name entry exists in res/values/strings.xml;
if missing, add app_name="SpeedCool" (or localized values) to strings.xml so the
manifest references a resource rather than a literal.
| val settingsFlow: Flow<Settings> = context.dataStore.data.map { prefs -> | ||
| Settings( | ||
| executorMode = prefs[KEY_EXECUTOR_MODE] ?: DEFAULT.executorMode, | ||
| activeProfile = prefs[KEY_ACTIVE_PROFILE] ?: DEFAULT.activeProfile, | ||
| autoStart = prefs[KEY_AUTO_START] ?: DEFAULT.autoStart, | ||
| backgroundService = prefs[KEY_BACKGROUND_SERVICE] ?: DEFAULT.backgroundService, | ||
| autoRamClean = prefs[KEY_AUTO_RAM_CLEAN] ?: DEFAULT.autoRamClean, | ||
| tempUnit = prefs[KEY_TEMP_UNIT] ?: DEFAULT.tempUnit, | ||
| ramCleanInterval = prefs[KEY_RAM_CLEAN_INTERVAL] ?: DEFAULT.ramCleanInterval, | ||
| learningEnabled = prefs[KEY_LEARNING_ENABLED] ?: DEFAULT.learningEnabled, | ||
| conflictAutoResolve = prefs[KEY_CONFLICT_AUTO_RESOLVE] ?: DEFAULT.conflictAutoResolve | ||
| ) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the target file (and print relevant lines around the snippet)
FILE="app/src/main/java/com/speedcool/app/data/SettingsData.kt"
test -f "$FILE" || { echo "Missing: $FILE"; exit 1; }
echo "== Snippet around settingsFlow =="
nl -ba "$FILE" | sed -n '40,90p'
# Find where `dataStore` is defined for `context` and whether exceptions are handled already
echo
echo "== Search for dataStore delegate/definition =="
rg -n "preferencesDataStore|DataStore<Preferences>|dataStore\s*=" app/src/main/java || true
echo
echo "== Search for catch/exception handling around context.dataStore.data usage =="
rg -n "context\.dataStore\.data|\.data\s*\n\s*\.catch|\.data\s*\n\s*catch|flow.*catch" app/src/main/java || trueRepository: Llucs/SpeedCool-Magisk-Module
Length of output: 168
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="app/src/main/java/com/speedcool/app/data/SettingsData.kt"
test -f "$FILE" || { echo "Missing: $FILE"; exit 1; }
echo "== settingsFlow snippet (lines 45-75) =="
sed -n '45,75p' "$FILE" | cat -n
echo
echo "== Exact lines containing settingsFlow =="
rg -n "settingsFlow" "$FILE" || true
echo
echo "== dataStore definitions / delegates =="
rg -n "preferencesDataStore|DataStore<Preferences>|dataStore\s*=" app/src/main/java || true
echo
echo "== Any exception handling near context.dataStore.data =="
rg -n "context\.dataStore\.data|\.data\s*\{|\.(catch|retry)|IOException|emptyPreferences" app/src/main/java || true
echo
echo "== Search for SettingsData.kt dataStore field/context setup =="
rg -n "class SettingsData|object SettingsData|SettingsData" "$FILE" || trueRepository: Llucs/SpeedCool-Magisk-Module
Length of output: 2564
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="app/src/main/java/com/speedcool/app/data/SettingsData.kt"
test -f "$FILE" || { echo "Missing: $FILE"; exit 1; }
echo "== Top of SettingsData.kt =="
sed -n '1,120p' "$FILE" | cat -nRepository: Llucs/SpeedCool-Magisk-Module
Length of output: 4951
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="app/src/main/java/com/speedcool/app/data/SettingsData.kt"
test -f "$FILE" || { echo "Missing: $FILE"; exit 1; }
echo "== Top of SettingsData.kt =="
sed -n '1,120p' "$FILE" | cat -nRepository: Llucs/SpeedCool-Magisk-Module
Length of output: 4951
🌐 Web query:
AndroidX DataStore Preferences data flow throws IOException corruptionHandler catch behavior
💡 Result:
In AndroidX DataStore, an IOException resulting from file corruption is typically wrapped as a CorruptionException [1]. DataStore does not automatically recover from this state; without a configured corruption handler, attempts to read from the corrupted file will propagate the exception [2]. To handle this gracefully, you must provide a corruptionHandler when creating your DataStore instance [2]. The handler is specifically designed to intercept the CorruptionException during read attempts [3][4][5]. Behavior of the CorruptionHandler: 1. If corruption is detected, the handler's handleCorruption method is invoked with the caught CorruptionException [1]. 2. The implementation of this method must return a default object (e.g., empty Preferences) that DataStore will attempt to write to disk, effectively resetting the file [1][2]. 3. If your handler implementation throws an exception, that exception will propagate and be thrown to the original caller [1]. 4. The handler must not interact with any DataStore API, as doing so can result in a deadlock [1]. Common implementation: Most developers use the ReplaceFileCorruptionHandler, which simply replaces the corrupted file with a fresh, empty state [6][2]. Example setup: val dataStore = PreferenceDataStoreFactory.create( corruptionHandler = ReplaceFileCorruptionHandler { emptyPreferences }, produceFile = { context.preferencesDataStoreFile("my_data") }) Note that if you catch IOException manually in your repository or collect block, ensure you correctly handle coroutine cancellation [7]. Avoid swallowing CancellationException in generic catch blocks, as this can break structured concurrency and lead to inconsistent app states [7].
Citations:
- 1: https://developer.android.com/reference/androidx/datastore/core/CorruptionHandler
- 2: https://developer.android.com/topic/libraries/architecture/datastore
- 3: https://developer.android.google.cn/reference/androidx/datastore/preferences/core/PreferenceDataStoreFactory
- 4: https://developer.android.google.cn/reference/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactory
- 5: https://developer.android.google.cn/reference/kotlin/androidx/datastore/preferences/package-summary
- 6: https://medium.com/@raj2512flair/exploring-preference-datastore-in-android-ed1aaec1ed3a
- 7: https://dev.to/software_mvp-factory/kotlin-coroutine-structured-concurrency-pitfalls-in-production-2el5
🌐 Web query:
preferencesDataStore(...) corruptionHandler ReplaceFileCorruptionHandler parameter availability Kotlin AndroidX DataStore
💡 Result:
Yes, the corruptionHandler parameter using ReplaceFileCorruptionHandler is available and supported in the preferencesDataStore delegate for AndroidX DataStore [1][2]. The preferencesDataStore property delegate allows you to configure a corruption handler to gracefully manage cases where DataStore encounters a CorruptionException (typically when data cannot be deserialized) [1][2]. You can provide a ReplaceFileCorruptionHandler, which attempts to replace the corrupted file with new, valid data generated by a provided callback [3][4]. Example usage: val Context.dataStore by preferencesDataStore( name = "settings", corruptionHandler = ReplaceFileCorruptionHandler { // Return default preferences to replace the corrupted file emptyPreferences }) When configured, if DataStore detects corruption during a read, it will invoke the handler's produceNewData callback and attempt to overwrite the corrupted file with the returned data, avoiding an application crash [5][4]. If no handler is provided, a CorruptionException will be thrown when corruption is encountered [5].
Citations:
- 1: https://developer.android.com/reference/androidx/datastore/preferences/PreferenceDataStoreDelegateKt
- 2: https://developer.android.google.cn/reference/kotlin/androidx/datastore/preferences/package-summary
- 3: https://developer.android.com/reference/kotlin/androidx/datastore/core/handlers/ReplaceFileCorruptionHandler
- 4: https://android.googlesource.com/platform/frameworks/support/+/f2e05c341382db64d127118a13451dcaa554b702/datastore/datastore-core/src/main/java/androidx/datastore/core/handlers/ReplaceFileCorruptionHandler.kt
- 5: https://developer.android.com/topic/libraries/architecture/datastore
Handle DataStore corruption/read failures for settingsFlow
At line 54, context.dataStore.data is mapped directly. If the on-disk preferences are corrupted (typically surfaced as CorruptionException), the settingsFlow collector can fail because the preferencesDataStore delegate has no corruptionHandler.
💡 Proposed fix
package com.speedcool.app.data
import android.content.Context
import androidx.datastore.core.DataStore
+import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
+import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
-private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "speedcool_settings")
+private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
+ name = "speedcool_settings",
+ corruptionHandler = ReplaceFileCorruptionHandler { emptyPreferences() }
+)
class SettingsData(private val context: Context) {🤖 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/speedcool/app/data/SettingsData.kt` around lines 54 -
66, The settingsFlow currently maps context.dataStore.data directly and will
crash on DataStore corruption; wrap the upstream flow with a catch that converts
CorruptionException into a safe fallback (e.g., emit(emptyPreferences())) before
the map so the collector won't fail. Concretely, update settingsFlow to use
context.dataStore.data.catch { e -> if (e is CorruptionException) { /* log */
emit(emptyPreferences()) } else throw e } . Then keep the existing .map { prefs
-> Settings(...) } unchanged; reference symbols: settingsFlow,
context.dataStore.data, CorruptionException, emptyPreferences().
|
|
||
| StatusSection("Dispositivo") { | ||
| StatusItem("Chipset", status.chipset) | ||
| StatusItem("Modo", executorModeDisplay(status.activeProfile)) |
There was a problem hiding this comment.
Clarify what "Modo" represents.
The label "Modo" (Mode) combined with the function name executorModeDisplay suggests this field displays the executor mode (Root vs. Shizuku). However, the function actually displays the performance profile (Eco/Balanced/Performance/Learning). This naming is potentially confusing.
Consider either:
- Change the label to "Perfil" (Profile) to match what's actually displayed
- Or if executor mode should be shown here, implement a separate function to display it
🤖 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/speedcool/app/ui/screens/StatusScreen.kt` at line 103,
The UI label "Modo" is misleading because StatusItem("Modo",
executorModeDisplay(status.activeProfile)) actually shows the performance
profile (status.activeProfile) not the executor mode; update the label to
"Perfil" to match the data, and optionally rename the helper to something
clearer (e.g., profileDisplay) or create a new function for executor mode
display — locate the StatusItem call and change the first argument from "Modo"
to "Perfil" and adjust or rename executorModeDisplay(status.activeProfile) to a
clearer identifier (or implement a separate executorModeDisplay function if you
intend to show executor mode instead).
| onStartService = { | ||
| scope.launch { | ||
| isServiceRunning = true | ||
| executorMode = engine.getExecutor()?.mode ?: "none" | ||
| } | ||
| }, | ||
| onStopService = { | ||
| isServiceRunning = false | ||
| }, |
There was a problem hiding this comment.
Start/stop actions do not control the Android service.
onStartService / onStopService only mutate local UI state (isServiceRunning) and never call Context.startForegroundService(...) / stopService(...), so the control button can report running while nothing is actually running.
Proposed fix
+import android.content.Context
+import android.content.Intent
+import androidx.compose.ui.platform.LocalContext
+import com.speedcool.app.service.SpeedCoolService
@@
fun SpeedCoolNavHost() {
+ val context = LocalContext.current
@@
onStartService = {
scope.launch {
- isServiceRunning = true
+ context.startForegroundService(Intent(context, SpeedCoolService::class.java))
+ isServiceRunning = true
executorMode = engine.getExecutor()?.mode ?: "none"
}
},
onStopService = {
+ context.stopService(Intent(context, SpeedCoolService::class.java))
isServiceRunning = false
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| onStartService = { | |
| scope.launch { | |
| isServiceRunning = true | |
| executorMode = engine.getExecutor()?.mode ?: "none" | |
| } | |
| }, | |
| onStopService = { | |
| isServiceRunning = false | |
| }, | |
| onStartService = { | |
| scope.launch { | |
| context.startForegroundService(Intent(context, SpeedCoolService::class.java)) | |
| isServiceRunning = true | |
| executorMode = engine.getExecutor()?.mode ?: "none" | |
| } | |
| }, | |
| onStopService = { | |
| context.stopService(Intent(context, SpeedCoolService::class.java)) | |
| isServiceRunning = false | |
| }, |
🤖 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/speedcool/app/ui/SpeedCoolNavHost.kt` around lines 83 -
91, onStartService/onStopService currently only flip the UI flag
(isServiceRunning) and never start/stop the Android service; update the handlers
in SpeedCoolNavHost so onStartService constructs the service Intent (targeting
your foreground service class), calls Context.startForegroundService(intent) (or
ContextCompat.startForegroundService if needed), then set isServiceRunning =
true and executorMode as done now, and have onStopService call
context.stopService(intent) and set isServiceRunning = false; locate the
anonymous handlers named onStartService and onStopService (and the usage of
scope.launch, isServiceRunning, executorMode, engine.getExecutor()) and inject
the startForegroundService/stopService calls using the Activity/Compose Context
available in this scope, handling any returned errors by logging and only
updating UI state after attempting to start/stop the service.
| Screen.Settings -> SettingsScreen( | ||
| autoStart = false, | ||
| backgroundService = true, | ||
| autoRamClean = true, | ||
| learningEnabled = true, | ||
| conflictAutoResolve = true, | ||
| tempUnit = "celsius", | ||
| ramCleanInterval = 180, | ||
| onAutoStartChange = {}, | ||
| onBackgroundServiceChange = {}, | ||
| onAutoRamCleanChange = {}, | ||
| onLearningEnabledChange = {}, | ||
| onConflictAutoResolveChange = {}, | ||
| onTempUnitChange = {}, | ||
| onRamCleanIntervalChange = {} | ||
| ) |
There was a problem hiding this comment.
Settings are wired as no-ops.
All settings callbacks are empty, so the screen appears interactive but cannot change app configuration/state.
Proposed fix direction
- Screen.Settings -> SettingsScreen(
- autoStart = false,
- backgroundService = true,
- autoRamClean = true,
- learningEnabled = true,
- conflictAutoResolve = true,
- tempUnit = "celsius",
- ramCleanInterval = 180,
- onAutoStartChange = {},
- onBackgroundServiceChange = {},
- onAutoRamCleanChange = {},
- onLearningEnabledChange = {},
- onConflictAutoResolveChange = {},
- onTempUnitChange = {},
- onRamCleanIntervalChange = {}
- )
+ Screen.Settings -> SettingsScreen(
+ autoStart = autoStart,
+ backgroundService = backgroundService,
+ autoRamClean = autoRamClean,
+ learningEnabled = learningEnabled,
+ conflictAutoResolve = conflictAutoResolve,
+ tempUnit = tempUnit,
+ ramCleanInterval = ramCleanInterval,
+ onAutoStartChange = { autoStart = it },
+ onBackgroundServiceChange = { backgroundService = it },
+ onAutoRamCleanChange = { autoRamClean = it },
+ onLearningEnabledChange = { learningEnabled = it },
+ onConflictAutoResolveChange = { conflictAutoResolve = it },
+ onTempUnitChange = { tempUnit = it },
+ onRamCleanIntervalChange = { ramCleanInterval = it }
+ )(Back these states with persistent settings store as next step.)
🤖 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/speedcool/app/ui/SpeedCoolNavHost.kt` around lines 107
- 122, The SettingsScreen call in SpeedCoolNavHost.kt currently passes hardcoded
state and empty callbacks (autoStart, backgroundService, autoRamClean,
learningEnabled, conflictAutoResolve, tempUnit, ramCleanInterval and
onAutoStartChange/onBackgroundServiceChange/onAutoRamCleanChange/onLearningEnabledChange/onConflictAutoResolveChange/onTempUnitChange/onRamCleanIntervalChange),
so user interactions are no-ops; instead, wire these props to real state and
handlers: obtain or inject a SettingsViewModel (or persistent settings
repository) in SpeedCoolNavHost, replace the hardcoded booleans/values with
ViewModel-backed LiveData/StateFlow values, and implement each callback to call
appropriate ViewModel methods (e.g., settingsViewModel.setAutoStart(enabled),
setBackgroundService(...), setRamCleanInterval(...), setTempUnit(...)) so
changes persist/update UI; ensure SettingsScreen receives current state from the
ViewModel rather than literal literals and that callbacks dispatch updates to
the store.
| #!/bin/sh | ||
| # Gradle wrapper script | ||
| if [ -n "$JAVA_HOME" ]; then | ||
| JAVACMD="$JAVA_HOME/bin/java" | ||
| else | ||
| JAVACMD=java | ||
| fi | ||
| DIR=$(dirname "$0") | ||
| exec "$JAVACMD" \ | ||
| -Dorg.gradle.appname=gradlew \ | ||
| -classpath "$DIR/gradle/wrapper/gradle-wrapper.jar" \ | ||
| org.gradle.wrapper.GradleWrapperMain "$@" |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Use the official Gradle wrapper script.
This is a minimal custom wrapper script instead of the standard Gradle-generated gradlew. The official wrapper includes additional features like Darwin/Cygwin detection, proper argument parsing, Gradle project location resolution, and better error handling. Using the official wrapper ensures consistency and maintainability.
Generate the official wrapper by running:
gradle wrapper --gradle-version 8.5🤖 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 `@gradlew` around lines 1 - 12, The current gradlew script is a minimal custom
wrapper (uses variables like JAVACMD, DIR and directly invokes
org.gradle.wrapper.GradleWrapperMain) and should be replaced with the official
Gradle-generated wrapper that includes Darwin/Cygwin detection, robust arg
parsing and error handling; regenerate and commit the proper wrapper by running
the Gradle wrapper task (e.g., run gradle wrapper --gradle-version 8.5) which
will produce the full gradlew script, gradle/wrapper/gradle-wrapper.jar and
gradle/wrapper/gradle-wrapper.properties to replace the current custom script.
| @@ -0,0 +1,92 @@ | |||
| @rem | |||
There was a problem hiding this comment.
Fix Unix line endings in Windows batch file.
The batch file uses Unix line endings (LF) instead of Windows line endings (CRLF), which will cause script failures on Windows due to batch parser bugs with GOTO/CALL label parsing. This will break builds on Windows systems.
Convert the file to CRLF line endings:
#!/bin/bash
# Convert gradlew.bat to Windows line endings
unix2dos gradlew.bat
# Or configure git to handle line endings automatically
git config core.autocrlf true🧰 Tools
🪛 Blinter (1.0.112)
[error] 1-1: Unix line endings detected. Explanation: Batch file uses Unix line endings (LF-only) which can cause GOTO/CALL label parsing failures and script malfunction due to Windows batch parser 512-byte boundary bugs. Recommendation: Convert file to Windows line endings (CRLF). Use tools like dos2unix, notepad++, or configure git with 'git config core.autocrlf true'. Context: File uses Unix line endings (LF-only) - 92 LF sequences found
(E018)
🤖 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 `@gradlew.bat` at line 1, The gradlew.bat file currently has Unix (LF) line
endings which will break Windows batch parsing; update gradlew.bat to use CRLF
line endings so Windows cmd handles GOTO/CALL labels correctly. Fix by
converting the file to CRLF (e.g., run a unix2dos conversion or change the
file's EOL in your editor) and/or add a .gitattributes rule (e.g., set
gradlew.bat to text eol=crlf) or enable core.autocrlf so future commits preserve
CRLF; commit the updated gradlew.bat with CRLF line endings.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
app/src/main/java/com/speedcool/app/root/ShellExecutor.kt (2)
18-29: 💤 Low valueProcess and stream resources may leak on exception.
If an exception occurs after starting the process but before
waitFor(), the process and its streams are not explicitly closed. Consider wrapping inprocess.use {}or a try-finally withprocess.destroy().Additionally, reading
inputStreamfully beforeerrorStreamcan deadlock if stderr fills its buffer first (uncommon for small outputs, but possible for verbose errors).♻️ Safer pattern using concurrent stream consumption
override suspend fun execute(command: String): Result<String> = withContext(Dispatchers.IO) { try { val process = Runtime.getRuntime().exec(arrayOf("su", "-c", command)) - val output = process.inputStream.bufferedReader().readText().trim() - val error = process.errorStream.bufferedReader().readText().trim() - val exitCode = process.waitFor() - if (exitCode == 0) Result.success(output) - else Result.failure(RuntimeException("Exit $exitCode: $error")) + try { + val output = process.inputStream.bufferedReader().readText().trim() + val error = process.errorStream.bufferedReader().readText().trim() + val exitCode = process.waitFor() + if (exitCode == 0) Result.success(output) + else Result.failure(RuntimeException("Exit $exitCode: $error")) + } finally { + process.destroy() + } } catch (e: Exception) { Result.failure(e) } }🤖 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/speedcool/app/root/ShellExecutor.kt` around lines 18 - 29, In ShellExecutor.execute, the started Process and its streams (process, inputStream, errorStream) can leak or deadlock; change the implementation to ensure the Process is closed in all cases (e.g., use process.use { } or a try/finally that calls process.destroy() and closes streams) and read stdout and stderr concurrently (e.g., launch two coroutines or threads to read process.inputStream and process.errorStream into variables) before calling process.waitFor(), then return Result.success or Result.failure based on exitCode; make these changes inside the execute function to guarantee no resource leaks and avoid blocking on one stream.
128-128: 💤 Low valueHardcoded Portuguese string in error message.
The error message should use a string resource or English for consistency and maintainability across the codebase.
✏️ Suggested change
- throw RuntimeException("Nenhum método de execução disponível") + throw RuntimeException("No execution method available")🤖 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/speedcool/app/root/ShellExecutor.kt` at line 128, In ShellExecutor (locate the throw RuntimeException in ShellExecutor.kt), replace the hardcoded Portuguese message with a localizable string: either throw a RuntimeException using context.getString(R.string.no_execution_method_available) (and add a strings.xml entry named no_execution_method_available with an English value "No execution method available") or, if you prefer immediate fix, replace the literal with the English text "No execution method available"; ensure you update callers/construction to have access to a Context if you switch to getString.
🤖 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 @.github/workflows/android-app.yml:
- Around line 2-5: The workflow trigger currently only runs for branch
"app-v2.4" (on: push: branches: [ "app-v2.4" ]) which will skip CI on main after
merges; update the workflow triggers to include "main" and/or pull_request
targets so CI runs for merges and PRs (e.g., add "main" to the push branches
array and add a pull_request trigger) to ensure the pipeline protects main and
PRs.
---
Nitpick comments:
In `@app/src/main/java/com/speedcool/app/root/ShellExecutor.kt`:
- Around line 18-29: In ShellExecutor.execute, the started Process and its
streams (process, inputStream, errorStream) can leak or deadlock; change the
implementation to ensure the Process is closed in all cases (e.g., use
process.use { } or a try/finally that calls process.destroy() and closes
streams) and read stdout and stderr concurrently (e.g., launch two coroutines or
threads to read process.inputStream and process.errorStream into variables)
before calling process.waitFor(), then return Result.success or Result.failure
based on exitCode; make these changes inside the execute function to guarantee
no resource leaks and avoid blocking on one stream.
- Line 128: In ShellExecutor (locate the throw RuntimeException in
ShellExecutor.kt), replace the hardcoded Portuguese message with a localizable
string: either throw a RuntimeException using
context.getString(R.string.no_execution_method_available) (and add a strings.xml
entry named no_execution_method_available with an English value "No execution
method available") or, if you prefer immediate fix, replace the literal with the
English text "No execution method available"; ensure you update
callers/construction to have access to a Context if you switch to getString.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7731e3c5-5349-43ae-b46f-dd4b8bf15c0b
⛔ Files ignored due to path filters (1)
gradle/wrapper/gradle-wrapper.jaris excluded by!**/*.jar
📒 Files selected for processing (9)
.github/workflows/android-app.yml.gitignoreapp/README.mdapp/build.gradle.ktsapp/proguard-rules.proapp/src/main/AndroidManifest.xmlapp/src/main/java/com/speedcool/app/root/ShellExecutor.ktapp/src/main/java/com/speedcool/app/service/BootReceiver.ktapp/src/main/java/com/speedcool/app/service/SpeedCoolService.kt
✅ Files skipped from review due to trivial changes (1)
- app/README.md
| on: | ||
| workflow_dispatch: | ||
|
|
||
| concurrency: | ||
| group: speedcool-app-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| push: | ||
| branches: [ "app-v2.4" ] |
There was a problem hiding this comment.
Workflow trigger is scoped to app-v2.4, so CI may stop on main after merge.
If this workflow is meant to protect the mainline, add main (and usually PRs targeting main) to triggers.
Suggested minimal update
on:
workflow_dispatch:
+ pull_request:
+ branches: [ "main" ]
push:
- branches: [ "app-v2.4" ]
+ branches: [ "main", "app-v2.4" ]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| on: | |
| workflow_dispatch: | |
| concurrency: | |
| group: speedcool-app-${{ github.ref }} | |
| cancel-in-progress: true | |
| push: | |
| branches: [ "app-v2.4" ] | |
| on: | |
| workflow_dispatch: | |
| pull_request: | |
| branches: [ "main" ] | |
| push: | |
| branches: [ "main", "app-v2.4" ] |
🤖 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 @.github/workflows/android-app.yml around lines 2 - 5, The workflow trigger
currently only runs for branch "app-v2.4" (on: push: branches: [ "app-v2.4" ])
which will skip CI on main after merges; update the workflow triggers to include
"main" and/or pull_request targets so CI runs for merges and PRs (e.g., add
"main" to the push branches array and add a pull_request trigger) to ensure the
pipeline protects main and PRs.
Aplicativo Android SpeedCool v2.4 com suporte a Root e Shizuku
Summary by CodeRabbit
New Features
Documentation