Skip to content

feat: stop all servers in noti center#901

Merged
lollipopkit merged 1 commit intomainfrom
lollipopkit/issue891
Sep 6, 2025
Merged

feat: stop all servers in noti center#901
lollipopkit merged 1 commit intomainfrom
lollipopkit/issue891

Conversation

@lollipopkit
Copy link
Owner

@lollipopkit lollipopkit commented Sep 6, 2025

Fixes #891

Summary by Sourcery

Unify SSH session notifications into one merged notification with a Stop All button that broadcasts to Flutter, implement the stopAllConnections flow on both Android and Dart sides, and fix the success rate formatting.

New Features:

  • Add a "Stop All" action in the Android foreground notification to terminate all SSH sessions and stop the service

Bug Fixes:

  • Remove the percent symbol from the success rate display in ConnectionStatsPage

Enhancements:

  • Replace individual session notifications with a single merged notification featuring dynamic title, content text, chronometer, and expanded session list
  • Broadcast a STOP_ALL_CONNECTIONS intent from the service on stop and handle it in MainActivity to invoke a Flutter stopAllConnections method
  • Extend the Flutter MethodChans API to support a stopAllConnections callback and implement session_manager.stopAllConnections to disconnect and clear all sessions
@sourcery-ai
Copy link

sourcery-ai bot commented Sep 6, 2025

Reviewer's Guide

This PR introduces a centralized "Stop All" action in the notification center, refactors the foreground service to emit a merged notification for SSH sessions, and hooks that action through a broadcast receiver into Flutter's session manager, with a minor UI string tweak.

Sequence diagram for the new 'Stop All' notification action flow

sequenceDiagram participant User as actor User participant Notification as Notification Center participant ForegroundService as ForegroundService participant MainActivity as MainActivity participant Flutter as Flutter (SessionManager) User->>Notification: Presses "Stop All" button Notification->>ForegroundService: ACTION_STOP_FOREGROUND intent ForegroundService->>MainActivity: Broadcast STOP_ALL_CONNECTIONS MainActivity->>Flutter: invokeMethod("stopAllConnections") Flutter->>Flutter: TermSessionManager.stopAllConnections() Flutter->>ForegroundService: All sessions disconnected ForegroundService->>Notification: Update notification (no active sessions) 
Loading

Entity relationship diagram for session management entries

erDiagram TERM_SESSION_MANAGER ||--o| SESSION_ENTRY : manages TERM_SESSION_MANAGER { entries activeId } SESSION_ENTRY { id disconnect } 
Loading

Updated class diagram for MethodChans and TermSessionManager

classDiagram class MethodChans { +registerHandler(onDisconnect: Function, onStopAll: VoidCallback?) } class TermSessionManager { +init() +stopAllConnections() +add() } MethodChans --> TermSessionManager : calls stopAllConnections via handler 
Loading

File-Level Changes

Change Details Files
Add 'Stop All' broadcast and Flutter channel integration
  • Send STOP_ALL_CONNECTIONS broadcast when stop action is triggered in ForegroundService
  • Register BroadcastReceiver in MainActivity to catch STOP_ALL_CONNECTIONS and invoke MethodChannel
  • Unregister receiver on activity destroy
android/app/src/main/kotlin/tech/lolli/toolbox/ForegroundService.kt
android/app/src/main/kotlin/tech/lolli/toolbox/MainActivity.kt
Refactor notification construction to a merged style
  • Replace createSummaryNotification with createMergedNotification accepting session list
  • Compute dynamic title and content based on session count
  • Use chronometer and inbox style for multiple sessions
  • Build single notification instead of per-session postings
android/app/src/main/kotlin/tech/lolli/toolbox/ForegroundService.kt
Unify notification IDs and cleanup stale notifications
  • Rename SUMMARY_ID to NOTIFICATION_ID and use consistently for startForeground/notify/cancel
  • Cancel all existing individual notifications and clear mapping on update
  • Remove GROUP_KEY and per-session postedIds logic
android/app/src/main/kotlin/tech/lolli/toolbox/ForegroundService.kt
Extend Flutter session manager to support stopAllConnections
  • Add stopAllConnections method in TermSessionManager to disconnect all and clear entries
  • Update MethodChans.registerHandler signature to accept an optional onStopAll callback
  • Invoke onStopAll from native channel handler
lib/data/ssh/session_manager.dart
lib/core/chan.dart
Minor UI fix in connection stats
  • Remove trailing '%' in success rate display
lib/view/page/server/connection_stats.dart

Assessment against linked issues

Issue Objective Addressed Explanation
#891 Add a button to exit (stop) all services in the notification center.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • Use LocalBroadcastManager or set the Intent’s package/permission when broadcasting STOP_ALL_CONNECTIONS to avoid exposing a public broadcast and ensure it’s scoped to your app.
  • Consider extracting the common Notification.Builder setup (channel, icon, flags) into a shared helper to reduce duplication in createMergedNotification.
  • Move hardcoded strings such as “Stop All” and notification titles/text into Android string resources to improve localization support.
Prompt for AI Agents
Please address the comments from this code review: ## Overall Comments - Use LocalBroadcastManager or set the Intent’s package/permission when broadcasting STOP_ALL_CONNECTIONS to avoid exposing a public broadcast and ensure it’s scoped to your app. - Consider extracting the common Notification.Builder setup (channel, icon, flags) into a shared helper to reduce duplication in createMergedNotification. - Move hardcoded strings such as “Stop All” and notification titles/text into Android string resources to improve localization support. ## Individual Comments ### Comment 1 <location> `lib/data/ssh/session_manager.dart:67` </location> <code_context> } + /// Called when Android notification "Stop All" button is pressed + static void stopAllConnections() { + // Disconnect all sessions + final disconnectCallbacks = _entries.values.map((e) => e.disconnect).where((cb) => cb != null).toList(); </code_context> <issue_to_address> Consider handling disconnect errors individually in stopAllConnections. Currently, if a disconnect callback throws an error, subsequent sessions may not be disconnected. Wrapping each callback in a try/catch will allow all sessions to be processed regardless of individual errors. </issue_to_address> <suggested_fix> <<<<<<< SEARCH for (final disconnect in disconnectCallbacks) { disconnect!(); } ======= for (final disconnect in disconnectCallbacks) {  try {  disconnect!();  } catch (e, stack) {  // Optionally log the error, e.g. using print or a logger  print('Error disconnecting session: $e\n$stack');  } } >>>>>>> REPLACE  </suggested_fix>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Comment on lines +70 to +72
for (final disconnect in disconnectCallbacks) {
disconnect!();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider handling disconnect errors individually in stopAllConnections.

Currently, if a disconnect callback throws an error, subsequent sessions may not be disconnected. Wrapping each callback in a try/catch will allow all sessions to be processed regardless of individual errors.

Suggested change
for (final disconnect in disconnectCallbacks) {
disconnect!();
}
for (final disconnect in disconnectCallbacks) {
try {
disconnect!();
} catch (e, stack) {
// Optionally log the error, e.g. using print or a logger
print('Error disconnecting session: $e\n$stack');
}
}
@lollipopkit lollipopkit merged commit 05a9277 into main Sep 6, 2025
3 checks passed
@lollipopkit lollipopkit deleted the lollipopkit/issue891 branch September 6, 2025 06:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant