ADFA-3270: Add erase installation file when done button to Plugin Manager#1119
Conversation
📝 Walkthrough
Risks / Best-practice notes:
WalkthroughReplaced legacy file-pick flow with ActivityResultContracts.OpenDocument, added an install confirmation dialog with a "delete source after install" checkbox, migrated UI effects to use string resource IDs (with optional format args), and implemented conditional deletion of the source document in the ViewModel using DocumentsContract. Changes
Sequence DiagramsequenceDiagram
participant User
participant Activity
participant Dialog
participant ViewModel
participant FileSystem
participant UI
User->>Activity: Start plugin install
Activity->>Activity: Launch OpenDocument picker
User->>Activity: Select file (Uri)
Activity->>Activity: takePersistableUriPermission(uri)
Activity->>Dialog: Show install confirmation (checkbox)
User->>Dialog: Confirm with deleteFlag
Dialog->>Activity: Return uri + deleteFlag
Activity->>ViewModel: Emit InstallPlugin(uri, deleteFlag)
ViewModel->>FileSystem: Install plugin from uri
ViewModel->>UI: Emit ShowSuccess(res_id) or ShowError(res_id, args)
alt deleteFlag == true
ViewModel->>FileSystem: DocumentsContract.deleteDocument(uri)
alt delete success
ViewModel->>UI: (no-op or success)
else delete fails
ViewModel->>UI: Emit ShowError(delete_error_res_id, args)
end
end
UI->>User: Display message
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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.
Actionable comments posted: 3
🤖 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/activities/PluginManagerActivity.kt`:
- Around line 48-59: The plugin picker currently accepts any MIME type and
forwards arbitrary files to showInstallConfirmation, causing later APK fallback;
update the registerForActivityResult/OpenDocument usage (pluginPickerLauncher)
to limit MIME types to known plugin formats (e.g.,
"application/vnd.android.package-archive" and any other supported plugin
MIME(s)) and, in the callback before calling showInstallConfirmation(uri),
validate the selected file's extension or MIME (use contentResolver.getType(uri)
and/or extract the filename/extension from DocumentFile.fromSingleUri or query
display name) and immediately reject/notify the user if it is unsupported; apply
the same validation logic to the other picker path referenced at lines 217-219
so unsupported files never enter the install confirmation flow.
In `@app/src/main/java/com/itsaky/androidide/ui/models/PluginManagerUiState.kt`:
- Around line 10-17: The current logic makes showEmptyState false when hasError
is true, which hides the persistent empty/error UI in
PluginManagerActivity.updateUI() and leaves the screen blank; fix by adding a
dedicated error flag (e.g., val showErrorState) or changing showEmptyState to
account for errors so the empty/error view remains visible when
plugins.isEmpty() && !isLoading and isPluginManagerAvailable, and expose that
flag for PluginManagerActivity.updateUI() to render error copy and retry actions
(reference: hasError, showEmptyState, plugins, isLoading,
isPluginManagerAvailable, PluginManagerActivity.updateUI(), emptyState).
In
`@app/src/main/java/com/itsaky/androidide/viewmodels/PluginManagerViewModel.kt`:
- Around line 263-280: Move the restart prompt emission to after the source
deletion attempt and ensure deletion errors cannot escape to the install failure
path: in the onSuccess block emit ShowSuccess and call loadPlugins() first, then
if (deleteSourceAfterInstall) call deleteSourceDocument(uri) inside its own
try/catch (or make deleteSourceDocument swallow/log all exceptions and return a
success/failure flag) so no exception propagates; only after that completion
send PluginManagerUiEffect.ShowRestartPrompt. Reference:
deleteSourceAfterInstall, deleteSourceDocument(uri),
PluginManagerUiEffect.ShowSuccess, loadPlugins(), and
PluginManagerUiEffect.ShowRestartPrompt.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 8b678516-28b8-4a9e-b9e3-4bc7480df67a
📒 Files selected for processing (5)
app/src/main/java/com/itsaky/androidide/activities/PluginManagerActivity.ktapp/src/main/java/com/itsaky/androidide/ui/models/PluginManagerUiState.ktapp/src/main/java/com/itsaky/androidide/viewmodels/PluginManagerViewModel.ktapp/src/main/res/layout/dialog_install_plugin.xmlresources/src/main/res/values/strings.xml
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
app/src/main/java/com/itsaky/androidide/viewmodels/PluginManagerViewModel.kt (1)
261-267:⚠️ Potential issue | 🟠 MajorEmit the restart prompt only after the source cleanup attempt finishes.
ShowRestartPromptis still sent beforedeleteSourceDocument(uri). If the user restarts immediately, the optional delete can be interrupted, and any delete-failure message can end up hidden behind the modal even though install already succeeded.Suggested diff
Log.d(TAG, "Plugin installed successfully") _uiEffect.trySend(PluginManagerUiEffect.ShowSuccess(R.string.msg_plugin_installed)) loadPlugins() - _uiEffect.trySend(PluginManagerUiEffect.ShowRestartPrompt) if (deleteSourceAfterInstall) { deleteSourceDocument(uri) } + + _uiEffect.trySend(PluginManagerUiEffect.ShowRestartPrompt)🤖 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/viewmodels/PluginManagerViewModel.kt` around lines 261 - 267, The restart prompt is currently emitted before the optional source cleanup, which can cause the delete to be interrupted; change the sequence so that when deleteSourceAfterInstall is true you first call and await deleteSourceDocument(uri) (or handle its completion) and only after that call _uiEffect.trySend(PluginManagerUiEffect.ShowRestartPrompt); keep the existing success toast and loadPlugins() order, but move the _uiEffect.trySend(PluginManagerUiEffect.ShowRestartPrompt) to after the deleteSourceDocument(uri) completion/await to ensure delete failures can surface before showing the restart modal.
🤖 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/activities/PluginManagerActivity.kt`:
- Around line 58-72: The code currently calls
contentResolver.takePersistableUriPermission immediately after file selection;
instead, defer taking the persistable grant until the user explicitly confirms
in showInstallConfirmation, and ensure any grant is revoked in all exit paths
(unsupported file, canceled dialog, failed install). Move the call to
contentResolver.takePersistableUriPermission into the confirm/positive callback
inside showInstallConfirmation (or a helper invoked on confirm), and add a
paired revokePersistableUriPermission call in the cleanup paths (on cancel, on
unsupported file before returning, and always after install attempt completes),
and update deleteSourceDocument()/deleteSourceAfterInstall handling so
deleteSourceDocument() is only called after a successful install but revocation
runs unconditionally to avoid stale permissions.
---
Duplicate comments:
In
`@app/src/main/java/com/itsaky/androidide/viewmodels/PluginManagerViewModel.kt`:
- Around line 261-267: The restart prompt is currently emitted before the
optional source cleanup, which can cause the delete to be interrupted; change
the sequence so that when deleteSourceAfterInstall is true you first call and
await deleteSourceDocument(uri) (or handle its completion) and only after that
call _uiEffect.trySend(PluginManagerUiEffect.ShowRestartPrompt); keep the
existing success toast and loadPlugins() order, but move the
_uiEffect.trySend(PluginManagerUiEffect.ShowRestartPrompt) to after the
deleteSourceDocument(uri) completion/await to ensure delete failures can surface
before showing the restart modal.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d8dcfbea-7b8a-4d13-a2f4-ece090e2fd46
📒 Files selected for processing (4)
app/src/main/java/com/itsaky/androidide/activities/PluginManagerActivity.ktapp/src/main/java/com/itsaky/androidide/ui/models/PluginManagerUiState.ktapp/src/main/java/com/itsaky/androidide/viewmodels/PluginManagerViewModel.ktresources/src/main/res/values/strings.xml
🚧 Files skipped from review as they are similar to previous changes (1)
- resources/src/main/res/values/strings.xml
Screen.Recording.2026-03-25.at.12.36.21.mov