ADFA-3007 | Add proactive low storage check and dialog#1011
Conversation
📝 WalkthroughRelease Notes - Low Storage Check and Prevention Dialog (ADFA-3007)New Features
Technical Changes
|
| Cohort / File(s) | Summary |
|---|---|
Storage Utilities app/src/main/java/com/itsaky/androidide/utils/StorageUtils.kt |
New helpers: hasEnoughStorageAvailable() checks device free bytes via StatFs, and getMinimumStorageNeeded() returns 4GB or 6GB based on feature flag. |
Low-Storage Dialog UI app/src/main/java/com/itsaky/androidide/ui/StorageDialog.kt |
New Activity.showLowStorageDialog() extension showing a non-cancelable AlertDialog with actions to close app or open internal storage settings. |
Splash Activity app/src/main/java/com/itsaky/androidide/activities/SplashActivity.kt |
Added early call to hasEnoughStorageAvailable() in onCreate(); shows low-storage dialog and returns if insufficient, preserving existing downstream logic otherwise. |
ViewModel update app/src/main/java/com/itsaky/androidide/viewmodel/InstallationViewModel.kt |
Replaced internal minimum-storage constants/logic with call to getMinimumStorageNeeded() when computing required storage bytes. |
Resources resources/src/main/res/values/strings.xml |
Added strings: err_insufficient_storage_title, err_insufficient_storage_msg, action_close_app, action_free_up_space used by the dialog. |
Sequence Diagram
sequenceDiagram
participant SA as SplashActivity
participant SU as StorageUtils
participant OS as OS/Environment
participant SD as StorageDialog
participant Sys as System
SA->>SU: hasEnoughStorageAvailable()
SU->>OS: Query data dir via StatFs
OS-->>SU: availableBlocksLong & blockSizeLong
SU->>SU: calculate availableBytes
SU->>SU: getMinimumStorageNeeded()
SU-->>SA: return Boolean (enough? )
alt Insufficient storage
SA->>SD: showLowStorageDialog()
SD->>Sys: display AlertDialog (non-cancelable)
Sys-->>SD: user action
alt Close app
SD->>SA: finishAndRemoveTask()
else Open storage settings
SD->>Sys: Intent ACTION_INTERNAL_STORAGE_SETTINGS (fallback ACTION_SETTINGS)
SD->>SA: finishAffinity()
end
else Sufficient storage
SA->>SA: continue initialization
end
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
- feat(ADFA-2994): Increase minimum storage needed for install #987: Introduced feature-flagged minimum storage constants and computed minimumStorageNeeded in InstallationViewModel; this PR centralizes that logic into
getMinimumStorageNeeded()and updates ViewModel usage.
Suggested reviewers
- dara-abijo-adfa
- Daniel-ADFA
- itsaky-adfa
Poem
🐰
I hopped in to check the burrow's floor,
Four gigs or six — we count once more.
If crumbs are few and space is low,
I tap the door and say "time to go!"
Hop safe, clear space, and onward we flow.
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | ✅ Passed | The title 'ADFA-3007 | Add proactive low storage check and dialog' directly and clearly describes the main change: adding a proactive storage check and dialog to prevent crashes. |
| Description check | ✅ Passed | The description comprehensively explains the changeset, including the problem being solved (SQLiteFullException crashes), the solution approach (proactive storage check at SplashActivity), and implementation details across multiple files. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
- 📝 Generate docstrings (stacked PR)
- 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
fix/ADFA-3007-add-low-storage-dialog
Tip
Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.
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 @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
app/src/main/java/com/itsaky/androidide/utils/StorageUtils.kt (1)
15-23: Semicolons and simplification opportunity ingetMinimumStorageNeeded.Lines 20 and 22 use Java-style trailing semicolons, which are not idiomatic Kotlin. The whole function can be expressed more concisely.
♻️ Proposed refactor
-fun getMinimumStorageNeeded(): Long { - val minimumStorageStableGB = 4L - val minimumStorageExperimentalGB = 6L - - if (FeatureFlags.isExperimentsEnabled) { - return minimumStorageExperimentalGB; - } - return minimumStorageStableGB; -} +fun getMinimumStorageNeeded(): Long = + if (FeatureFlags.isExperimentsEnabled) 6L else 4L🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/itsaky/androidide/utils/StorageUtils.kt` around lines 15 - 23, The function getMinimumStorageNeeded has Java-style trailing semicolons and can be simplified; remove the semicolons after the return statements and convert the block to a concise expression body that returns either minimumStorageExperimentalGB or minimumStorageStableGB based on FeatureFlags.isExperimentsEnabled (you can inline the constants or keep the existing minimumStorageStableGB/minimumStorageExperimentalGB names), so replace the current multiline function with a single-expression function using an if expression referencing getMinimumStorageNeeded, minimumStorageStableGB, minimumStorageExperimentalGB, and FeatureFlags.isExperimentsEnabled.app/src/main/java/com/itsaky/androidide/ui/StorageDialog.kt (1)
4-4: Preferandroidx.appcompat.app.AlertDialogoverandroid.app.AlertDialog.The platform
AlertDialogdoes not respect your app's Material/AppCompat theme on all API levels. Using the AppCompat variant ensures consistent styling.♻️ Proposed change
-import android.app.AlertDialog +import androidx.appcompat.app.AlertDialog🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/itsaky/androidide/ui/StorageDialog.kt` at line 4, Replace the platform AlertDialog import with the AppCompat variant to ensure consistent theming: change the import of android.app.AlertDialog to androidx.appcompat.app.AlertDialog and ensure any uses in StorageDialog (e.g., AlertDialog.Builder(...) or return types/fields named AlertDialog) reference the AppCompat class; if you construct dialogs from a Context that may not be AppCompatActivity, use the AppCompat-compatible builder pattern (or pass an AppCompatContext) so the androidx.appcompat.app.AlertDialog is used throughout.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/main/java/com/itsaky/androidide/ui/StorageDialog.kt`:
- Around line 22-25: In StorageDialog.kt inside the setNegativeButton lambda
(the code block that currently calls
startActivity(Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS)) and
finishAffinity()), wrap the startActivity call in a try-catch that catches
android.content.ActivityNotFoundException; on failure construct and launch a
fallback Intent (e.g., Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS or
Settings.ACTION_SETTINGS) and only then call finishAffinity(); ensure both the
primary and fallback intents use startActivity safely and that the catch does
not suppress the finishAffinity() call.
In `@app/src/main/java/com/itsaky/androidide/utils/StorageUtils.kt`:
- Around line 6-13: The current hasEnoughStorageAvailable() function swallows
StatFs/I/O exceptions and returns true, which is unsafe; change the exception
handling in hasEnoughStorageAvailable to treat any exception as "not enough
storage" by returning false (and optionally log the exception for diagnostics)
instead of true, while keeping use of Environment.getDataDirectory(),
StatFs(dataDir.path), availableBlocksLong/blockSizeLong and
getMinimumStorageNeeded().gigabytesToBytes() intact so callers correctly avoid
heavy I/O when storage checks fail.
---
Nitpick comments:
In `@app/src/main/java/com/itsaky/androidide/ui/StorageDialog.kt`:
- Line 4: Replace the platform AlertDialog import with the AppCompat variant to
ensure consistent theming: change the import of android.app.AlertDialog to
androidx.appcompat.app.AlertDialog and ensure any uses in StorageDialog (e.g.,
AlertDialog.Builder(...) or return types/fields named AlertDialog) reference the
AppCompat class; if you construct dialogs from a Context that may not be
AppCompatActivity, use the AppCompat-compatible builder pattern (or pass an
AppCompatContext) so the androidx.appcompat.app.AlertDialog is used throughout.
In `@app/src/main/java/com/itsaky/androidide/utils/StorageUtils.kt`:
- Around line 15-23: The function getMinimumStorageNeeded has Java-style
trailing semicolons and can be simplified; remove the semicolons after the
return statements and convert the block to a concise expression body that
returns either minimumStorageExperimentalGB or minimumStorageStableGB based on
FeatureFlags.isExperimentsEnabled (you can inline the constants or keep the
existing minimumStorageStableGB/minimumStorageExperimentalGB names), so replace
the current multiline function with a single-expression function using an if
expression referencing getMinimumStorageNeeded, minimumStorageStableGB,
minimumStorageExperimentalGB, and FeatureFlags.isExperimentsEnabled.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
app/src/main/java/com/itsaky/androidide/activities/SplashActivity.ktapp/src/main/java/com/itsaky/androidide/ui/StorageDialog.ktapp/src/main/java/com/itsaky/androidide/utils/StorageUtils.ktapp/src/main/java/com/itsaky/androidide/viewmodel/InstallationViewModel.ktresources/src/main/res/values/strings.xml
Shows a blocking dialog in SplashActivity if the device storage is full.
… fix the exception handling in `hasEnoughStorageAvailable` method
4bb126b to
a58b488
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/com/itsaky/androidide/viewmodel/InstallationViewModel.kt (1)
135-148:⚠️ Potential issue | 🟠 Major
StatFsingetStorageInfois unguarded — can crash when called fromstartIdeSetup.
StatFs(internalStoragePath)can throw (e.g.,IllegalArgumentExceptioniffilesDirdoesn't exist yet, or an I/O error).getStorageInfohas notry/catch, and its callercheckStorageAndNotifyis invoked at line 63 ofstartIdeSetupbefore theviewModelScope.launch { try { … } catch { … } }block, so the exception propagates completely uncaught. This is the same class of crash the PR aims to prevent, and it's inconsistent withhasEnoughStorageAvailable()inStorageUtils.ktwhich wraps its ownStatFscall.🛡️ Proposed fix
private fun getStorageInfo(context: Context): StorageInfo { val internalStoragePath = context.filesDir.path - val stat = StatFs(internalStoragePath) - - val availableStorageInBytes = stat.availableBlocksLong * stat.blockSizeLong + val availableStorageInBytes = try { + val stat = StatFs(internalStoragePath) + stat.availableBlocksLong * stat.blockSizeLong + } catch (_: Exception) { + 0L // treat unreadable storage as 0 bytes available + } val requiredStorageInBytes = getMinimumStorageNeeded().gigabytesToBytes()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/itsaky/androidide/viewmodel/InstallationViewModel.kt` around lines 135 - 148, getStorageInfo currently constructs StatFs(internalStoragePath) without protection, which can throw and crash callers like startIdeSetup->checkStorageAndNotify; wrap the StatFs construction and subsequent reads in a try/catch (catch IllegalArgumentException/IOException/RuntimeException or a general Exception) inside getStorageInfo and on error log the exception and return a safe fallback StorageInfo indicating low storage (e.g., isLowStorage = true, availableStorageInBytes = 0L, additionalBytesNeeded = getMinimumStorageNeeded().gigabytesToBytes()); keep references to getStorageInfo, checkStorageAndNotify, startIdeSetup and compare behavior to StorageUtils.hasEnoughStorageAvailable when implementing the guard.
🧹 Nitpick comments (1)
app/src/main/java/com/itsaky/androidide/utils/StorageUtils.kt (1)
15-22: Optional: remove spurious semicolons and simplify to expression function.Line 20 has a trailing semicolon (non-idiomatic Kotlin), and the whole body can collapse to a single expression.
♻️ Suggested simplification
-fun getMinimumStorageNeeded(): Long { - val minimumStorageStableGB = 4L - val minimumStorageExperimentalGB = 6L - - if (FeatureFlags.isExperimentsEnabled) { - return minimumStorageExperimentalGB; - } - return minimumStorageStableGB; -} +private const val MINIMUM_STORAGE_STABLE_GB = 4L +private const val MINIMUM_STORAGE_EXPERIMENTAL_GB = 6L + +fun getMinimumStorageNeeded(): Long = + if (FeatureFlags.isExperimentsEnabled) MINIMUM_STORAGE_EXPERIMENTAL_GB else MINIMUM_STORAGE_STABLE_GB🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/itsaky/androidide/utils/StorageUtils.kt` around lines 15 - 22, The function getMinimumStorageNeeded contains non-idiomatic trailing semicolons and a multi-statement body; simplify it to a single-expression function and remove the semicolons. Replace the if/return block with a concise expression that returns either minimumStorageExperimentalGB or minimumStorageStableGB based on FeatureFlags.isExperimentsEnabled (keeping the local constants minimumStorageStableGB and minimumStorageExperimentalGB or inlining them if preferred) and ensure no trailing semicolons remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@app/src/main/java/com/itsaky/androidide/viewmodel/InstallationViewModel.kt`:
- Around line 135-148: getStorageInfo currently constructs
StatFs(internalStoragePath) without protection, which can throw and crash
callers like startIdeSetup->checkStorageAndNotify; wrap the StatFs construction
and subsequent reads in a try/catch (catch
IllegalArgumentException/IOException/RuntimeException or a general Exception)
inside getStorageInfo and on error log the exception and return a safe fallback
StorageInfo indicating low storage (e.g., isLowStorage = true,
availableStorageInBytes = 0L, additionalBytesNeeded =
getMinimumStorageNeeded().gigabytesToBytes()); keep references to
getStorageInfo, checkStorageAndNotify, startIdeSetup and compare behavior to
StorageUtils.hasEnoughStorageAvailable when implementing the guard.
---
Nitpick comments:
In `@app/src/main/java/com/itsaky/androidide/utils/StorageUtils.kt`:
- Around line 15-22: The function getMinimumStorageNeeded contains non-idiomatic
trailing semicolons and a multi-statement body; simplify it to a
single-expression function and remove the semicolons. Replace the if/return
block with a concise expression that returns either minimumStorageExperimentalGB
or minimumStorageStableGB based on FeatureFlags.isExperimentsEnabled (keeping
the local constants minimumStorageStableGB and minimumStorageExperimentalGB or
inlining them if preferred) and ensure no trailing semicolons remain.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
app/src/main/java/com/itsaky/androidide/activities/SplashActivity.ktapp/src/main/java/com/itsaky/androidide/ui/StorageDialog.ktapp/src/main/java/com/itsaky/androidide/utils/StorageUtils.ktapp/src/main/java/com/itsaky/androidide/viewmodel/InstallationViewModel.ktresources/src/main/res/values/strings.xml
🚧 Files skipped from review as they are similar to previous changes (2)
- app/src/main/java/com/itsaky/androidide/ui/StorageDialog.kt
- resources/src/main/res/values/strings.xml
Description
This PR fixes a critical issue where the application crashes with
SQLiteFullExceptionandENOSPCerrors when a user opens the app while their device storage is completely full. Instead of allowing background libraries (like WorkManager, Firebase, Room) and internal file extractors to fail in a cascading manner, we now proactively check for available storage at the earliest entry point (SplashActivity).If the device does not meet the minimum required storage (4GB for stable, 6GB for experimental), a non-cancelable
AlertDialogis shown to the user, preventing further heavy I/O operations and providing a direct action to open the device's storage settings to free up space.Details
StorageUtils.ktto calculate the available device storage.InstallationViewModelto use the centralizedgetMinimumStorageNeeded()method.Activity.showLowStorageDialog()extension function inStorageDialog.kt.strings.xmlto support future localization and prevent hardcoded texts.SplashActivitynow finishes cleanly without memory leaks when the user is blocked by the dialog.Before changes
Screen.Recording.2026-02-24.at.5.13.17.PM.mov
After changes
Screen.Recording.2026-02-24.at.5.05.06.PM.mov
Ticket
ADFA-3007
Also fixes:
ADFA-3018
Observation
The minimum storage threshold logic was also successfully decoupled so it can be reused across the app.