Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Journey improvements #159

Merged
merged 7 commits into from
Dec 12, 2024
Merged

Journey improvements #159

merged 7 commits into from
Dec 12, 2024

Conversation

cp-ishita-g
Copy link
Collaborator

@cp-ishita-g cp-ishita-g commented Dec 11, 2024

Summary by CodeRabbit

  • New Features

    • Introduced a new LocationManager for enhanced location handling.
    • Added reactive programming support for location updates.
    • Implemented location permission checks before starting tracking.
    • Added support for categorizing journeys by type (steady/moving).
  • Bug Fixes

    • Improved error handling related to location permissions.
  • Chores

    • Organized project structure to include new source files.
    • Updated .gitignore to exclude additional directories.

Copy link

coderabbitai bot commented Dec 11, 2024

Caution

Review failed

The pull request is closed.

Walkthrough

The changes in this pull request introduce a new LocationManager.swift file for managing location updates using a reactive programming approach with Combine. Modifications to AppDelegate.swift enhance location handling, including the addition of a method for registering location-related method calls. The main.dart file is updated to ensure location tracking starts only with the appropriate permissions. Additionally, the LocationManager class in location_manager.dart is adjusted to facilitate communication with native code for tracking control. Overall, the changes reflect a comprehensive update to location management across multiple files.

Changes

File Path Change Summary
app/ios/Runner.xcodeproj/project.pbxproj Added new file reference and build file entry for LocationManager.swift, included in the Sources build phase.
app/ios/Runner/AppDelegate.swift Removed locationManager, added cancellables, updated application(_:didFinishLaunchingWithOptions:) for location updates, added getLocationMethodRegistration(), and renamed geofencePluginRegistration() to registerLocationChannel().
app/ios/Runner/LocationManager.swift Introduced LocationsHandler class with methods for managing location updates, including starting/stopping updates and handling authorization changes.
app/lib/main.dart Updated method call handler to check for location permissions before starting tracking and modified _configureService and onStart methods accordingly.
data/lib/service/location_manager.dart Added locationMethodChannel constant and updated startService and stopTrackingService methods to invoke native tracking methods.
data/lib/api/location/journey/api_journey_service.dart Updated saveCurrentJourney method to include an optional type parameter for journey categorization.
data/lib/api/location/journey/journey.dart Added constants for JOURNEY_TYPE_STEADY and JOURNEY_TYPE_MOVING, and updated ApiLocationJourney to include a type parameter.
data/lib/api/location/journey/journey.freezed.dart Modified ApiLocationJourney and JourneyRoute classes to include a nullable type field with updated serialization methods.
data/lib/api/location/journey/journey.g.dart Added type field to JSON serialization and deserialization for _$LocationJourneyImpl.
data/lib/repository/journey_repository.dart Updated journey type checks to use constants instead of method calls, ensuring consistent journey type handling.
app/.gitignore Added entries .build/ and .swiftpm/ to be ignored by version control.
data/lib/storage/location_caches.dart Removed _lastJourneyUpdatedTime cache and related methods, streamlining the LocationCache class.

Possibly related PRs

Suggested reviewers

  • kaushiksaliya

Poem

🐰 In the meadow where bunnies play,
Location updates come out to stay.
With Combine in hand, we hop with glee,
Tracking the world, just you and me!
Permissions checked, we leap and bound,
In our code, new joys are found! 🌼


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3606062 and d8f9f7a.

📒 Files selected for processing (5)
  • app/.gitignore (1 hunks)
  • app/lib/ui/flow/journey/timeline/journey_timeline_screen.dart (2 hunks)
  • data/lib/api/location/journey/journey.dart (4 hunks)
  • data/lib/repository/journey_repository.dart (10 hunks)
  • data/lib/storage/location_caches.dart (1 hunks)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Outside diff range and nitpick comments (2)
app/lib/main.dart (2)

Line range hint 150-156: Consolidate permission checks and add error handling.

The permission check here duplicates logic from the main initialization. Additionally, service configuration lacks error handling.

Consider this improved implementation:

 void _configureService() async {
+  try {
     await bgService.configure(
       androidConfiguration: AndroidConfiguration(
         onStart: onStart,
         autoStart: false,
         isForegroundMode: true,
         notificationChannelId: NOTIFICATION_CHANNEL_ID,
         foregroundServiceNotificationId: NOTIFICATION_ID,
       ),
       iosConfiguration: IosConfiguration(autoStart: false),
     );
 
-    if (await Permission.location.isGranted) {
-      LocationManager.instance.startService();
-    }
+    // Let LocationManager handle its own permission checks
+    await LocationManager.instance.startService();
+  } catch (e) {
+    debugPrint('Failed to configure background service: $e');
+    // Consider showing user feedback
+  }
 }

Line range hint 170-199: Improve service lifecycle handling and error management.

The current implementation has several potential issues:

  1. Early return on permission check might leave service in inconsistent state
  2. Missing cleanup when service stops
  3. No error handling for Firebase initialization

Consider this more robust implementation:

 @pragma('vm:entry-point')
 Future<void> onStart(ServiceInstance service) async {
   WidgetsFlutterBinding.ensureInitialized();
-  if (!await Permission.location.isGranted) return;
+  
+  try {
+    await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
 
-  final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
+    final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
 
-  if (service is AndroidServiceInstance) {
-    if (await service.isForegroundService()) {
-      flutterLocalNotificationsPlugin.show(
-        NOTIFICATION_ID,
-        'Your space Location',
-        'Location is being tracked',
-        const NotificationDetails(
-          android: AndroidNotificationDetails(
-            NOTIFICATION_CHANNEL_ID,
-            'MY FOREGROUND SERVICE',
-            icon: "app_icon",
-            ongoing: true,
-            color: Color(0xFF1679AB),
-          ),
-        ),
-      );
-    }
-  }
+    if (service is AndroidServiceInstance && await service.isForegroundService()) {
+      if (await Permission.location.isGranted) {
+        await flutterLocalNotificationsPlugin.show(
+          NOTIFICATION_ID,
+          'Your space Location',
+          'Location is being tracked',
+          const NotificationDetails(
+            android: AndroidNotificationDetails(
+              NOTIFICATION_CHANNEL_ID,
+              'MY FOREGROUND SERVICE',
+              icon: "app_icon",
+              ongoing: true,
+              color: Color(0xFF1679AB),
+            ),
+          ),
+        );
+        
+        await LocationManager.instance.startTracking();
+      } else {
+        // Notify user about missing permission
+        service.stopSelf();
+        return;
+      }
+    }
 
-  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
-
-  LocationManager.instance.startTracking();
-
-  service.on('stopService').listen((event) {
-    service.stopSelf();
-  });
+    service.on('stopService').listen((event) async {
+      await LocationManager.instance.stopTracking();
+      await service.stopSelf();
+    });
+  } catch (e) {
+    debugPrint('Failed to start service: $e');
+    service.stopSelf();
+  }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 4a5b6f3 and 13ab708.

⛔ Files ignored due to path filters (2)
  • app/ios/Podfile.lock is excluded by !**/*.lock
  • app/pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • app/ios/Runner.xcodeproj/project.pbxproj (4 hunks)
  • app/ios/Runner/AppDelegate.swift (1 hunks)
  • app/ios/Runner/LocationManager.swift (1 hunks)
  • app/lib/main.dart (1 hunks)
  • data/lib/service/location_manager.dart (3 hunks)
🔇 Additional comments (1)
app/ios/Runner.xcodeproj/project.pbxproj (1)

Line range hint 249-249: LGTM: Deployment target is appropriate for location features.

The iOS deployment target is set to 14.0, which provides full support for all location-related features including background location updates and precise location.

Also applies to: 250-250, 251-251

app/lib/main.dart Show resolved Hide resolved
app/ios/Runner/AppDelegate.swift Show resolved Hide resolved
app/ios/Runner/AppDelegate.swift Show resolved Hide resolved
app/ios/Runner/AppDelegate.swift Show resolved Hide resolved
app/ios/Runner/LocationManager.swift Show resolved Hide resolved
app/ios/Runner/LocationManager.swift Outdated Show resolved Hide resolved
data/lib/service/location_manager.dart Show resolved Hide resolved
app/ios/Runner.xcodeproj/project.pbxproj Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (1)
app/lib/main.dart (1)

Background service implementation needs robust error handling and cleanup mechanisms

The review comment's concerns are valid based on the codebase analysis:

  1. The background service (onStart) lacks error handling for Firebase initialization and other operations
  2. No continuous permission monitoring or cleanup mechanism when permissions are revoked
  3. The service continues running even when initialization fails

Key findings:

  • LocationManager has stopTrackingService but it's not used in error scenarios
  • Firebase initialization is done without try-catch blocks
  • Permission checks are simple one-time checks without continuous monitoring
  • No comprehensive error handling in the background service entry point

Suggested changes remain valid and should be implemented as proposed in the review comment.

🔗 Analysis chain

Line range hint 170-199: Improve background service robustness and error handling.

Several potential issues in the background service implementation:

  1. Race condition: Permission could be revoked between check and service start
  2. Missing error handling for Firebase initialization
  3. No cleanup mechanism when permissions are revoked

Consider implementing this more robust approach:

@pragma('vm:entry-point')
Future<void> onStart(ServiceInstance service) async {
  WidgetsFlutterBinding.ensureInitialized();
-  if (!await Permission.location.isGranted) return;
+  try {
+    if (!await Permission.location.isGranted) {
+      await service.stopSelf();
+      return;
+    }
+
+    await Firebase.initializeApp(
+      options: DefaultFirebaseOptions.currentPlatform,
+    ).catchError((error) {
+      debugPrint('Failed to initialize Firebase: $error');
+      service.stopSelf();
+      return;
+    });
+
+    // Listen for permission changes
+    Permission.location.status.listen((status) {
+      if (!status.isGranted) {
+        LocationManager.instance.stopTracking();
+        service.stopSelf();
+      }
+    });

    // Rest of your implementation...
+  } catch (e) {
+    debugPrint('Background service error: $e');
+    await service.stopSelf();
+  }
}

Let's verify the current error handling implementation:


Let me gather more information about the background service implementation.


Let me check the actual implementation of the background service and location tracking.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check error handling in background service
echo "Checking error handling patterns..."
rg "try|catch|error" --type dart app/lib/service/

# Check permission handling
echo "Checking permission handling..."
rg "Permission.location" --type dart

Length of output: 1277


Script:

#!/bin/bash
# Check background service implementation
echo "Checking service files..."
fd "service|main" --type f --extension dart

echo "Checking Firebase initialization..."
rg "Firebase.initializeApp" --type dart -A 3

echo "Checking service stop/cleanup..."
rg "stopSelf|stopTracking" --type dart

Length of output: 2205


Script:

#!/bin/bash
# Check location manager implementation
echo "Checking LocationManager implementation..."
rg -A 10 "class LocationManager" --type dart

# Check background service entry point
echo "Checking background service entry point..."
rg -B 5 -A 10 "@pragma\('vm:entry-point'\)" --type dart

# Check error handling in location tracking
echo "Checking location tracking error handling..."
ast-grep --pattern 'try {
  $$$
} catch ($_, $_) {
  $$$
}'

Length of output: 2035

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13ab708 and 325a4e8.

⛔ Files ignored due to path filters (1)
  • app/pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • app/lib/main.dart (1 hunks)
🔇 Additional comments (1)
app/lib/main.dart (1)

43-47: Add error handling and runtime permission changes support.

The implementation still has the same potential issues as previously identified:

  1. Missing error handling for the invokeMethod call
  2. No handling of runtime permission changes
  3. One-time permission check might not be sufficient

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (4)
data/lib/api/location/journey/journey.dart (1)

11-12: Consider using enum instead of string constants

While string constants work, using an enum for journey types would provide better type safety and IDE support.

enum JourneyType {
  steady,
  moving
}
data/lib/api/location/journey/journey.g.dart (1)

Line range hint 41-62: Consider database migration strategy

The addition of the type field to existing journeys requires a migration strategy. Consider how to handle existing records that don't have this field.

  1. Add a database migration script to set default types for existing records
  2. Consider adding a version field to the journey model for future migrations
  3. Document the migration process in the PR description
data/lib/repository/journey_repository.dart (2)

Line range hint 80-99: Consider extracting magic number to a constant

While the type comparison change is good, the 5-minute timer duration should be extracted to a named constant for better maintainability.

 const MIN_TIME_DIFFERENCE = 5 * 60 * 1000; // 5 minutes
+const STEADY_LOCATION_TIMER_DURATION = 5; // minutes

 // In the Timer constructor
-Timer(const Duration(minutes: 5), () async {
+Timer(const Duration(minutes: STEADY_LOCATION_TIMER_DURATION), () async {

Line range hint 200-245: Consider simplifying the distance calculation logic

The distance calculation could be made more readable by extracting it into a helper method.

+private double calculateJourneyDistance(LocationData currentLocation, ApiLocationJourney lastKnownJourney) {
+  final referenceLocation = lastKnownJourney.type == JOURNEY_TYPE_STEADY
+      ? lastKnownJourney.toLocationFromSteadyJourney()
+      : lastKnownJourney.toLocationFromMovingJourney();
+  return _distanceBetween(currentLocation, referenceLocation);
+}

-double distance = lastKnownJourney.type == JOURNEY_TYPE_STEADY
-    ? _distanceBetween(geometricMedian ?? extractedLocation,
-        lastKnownJourney.toLocationFromSteadyJourney())
-    : _distanceBetween(geometricMedian ?? extractedLocation,
-        lastKnownJourney.toLocationFromMovingJourney());
+double distance = calculateJourneyDistance(geometricMedian ?? extractedLocation, lastKnownJourney);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 325a4e8 and 3606062.

📒 Files selected for processing (7)
  • app/ios/Runner/LocationManager.swift (1 hunks)
  • app/lib/ui/flow/journey/timeline/journey_timeline_screen.dart (2 hunks)
  • data/lib/api/location/journey/api_journey_service.dart (2 hunks)
  • data/lib/api/location/journey/journey.dart (4 hunks)
  • data/lib/api/location/journey/journey.freezed.dart (20 hunks)
  • data/lib/api/location/journey/journey.g.dart (2 hunks)
  • data/lib/repository/journey_repository.dart (10 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/ios/Runner/LocationManager.swift
🔇 Additional comments (8)
data/lib/api/location/journey/journey.dart (1)

Line range hint 44-52: Verify the impact of empty route list for steady journeys

The toRoute() method now returns an empty list for steady journeys. Ensure this change doesn't break any UI components or map visualizations that depend on this data.

✅ Verification successful

Based on the search results, I can see that the toRoute() method is used in several places within the codebase, particularly in the journey map visualization components. Let me analyze the key findings:

  1. The toRoute() method is used in JourneyMap widget (app/lib/ui/flow/journey/components/journey_map.dart) for:

    • Creating map bounds
    • Drawing polylines on the map
    • Calculating distances and zoom levels
  2. The empty route list for steady journeys appears to be an intentional design choice as:

    • Steady journeys represent stationary locations
    • The UI handles both steady and moving journeys differently
    • There are specific UI elements for displaying steady journey durations
  3. The code has proper null checks and handling for routes:

    • Map visualization components check journey types
    • Different UI representations for steady vs moving journeys
    • Proper bounds calculation considering empty routes

No changes needed - empty routes for steady journeys is the correct behavior

The empty route list for steady journeys is an intentional design choice that's properly handled throughout the codebase. The UI components are designed to handle both steady and moving journeys appropriately, with steady journeys being displayed as single points rather than routes.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for direct usage of toRoute() method
rg "toRoute\(\)" -A 3

# Search for map-related files that might use routes
fd -e dart -x rg "route|map|journey" {} \;

Length of output: 38447

data/lib/api/location/journey/journey.g.dart (1)

26-26: Verify build configuration for code generation

The generated code correctly includes the new type field. However, ensure the build configuration is properly set up to regenerate this file when the model changes.

Also applies to: 43-43

✅ Verification successful

Build configuration is properly set up for code generation

The build configuration is correctly set up with all necessary dependencies:

  • build_runner: ^2.4.8 is present in dev_dependencies
  • json_serializable: ^6.7.1 is configured for model generation
  • The data package has explicit JSON serialization settings in build.yaml
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for build configuration files
fd "build.yaml|pubspec.yaml" -x cat {}

# Verify build_runner dependency
rg "build_runner:" -A 5

Length of output: 9474

data/lib/repository/journey_repository.dart (3)

Line range hint 141-181: LGTM!

The addition of explicit journey type when saving is consistent with the new type-based approach and improves code clarity.


Line range hint 250-301: LGTM!

The addition of explicit journey type for moving users is consistent and well-implemented.


Line range hint 347-383: LGTM!

The addition of explicit journey type when saving stopped journeys is consistent and well-implemented.

app/lib/ui/flow/journey/timeline/journey_timeline_screen.dart (2)

Line range hint 130-143: LGTM!

The change to use direct type comparison improves code readability and maintains consistency with the repository layer changes.


202-202: LGTM!

The change to use direct type comparison in place info building is consistent with the overall changes.

data/lib/api/location/journey/journey.freezed.dart (1)

Line range hint 34-586: Verify freezed generation matches the schema

This is an auto-generated file. Please ensure that the source schema in journey.dart correctly defines the type field as a nullable String.

✅ Verification successful

Generated code correctly matches the schema definition

The freezed-generated code in journey.freezed.dart correctly implements the nullable String? type field as defined in the source schema journey.dart. The field is properly included in all the generated classes and implementations with the correct type and nullability.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the journey schema definition

# Check the journey.dart file for type field definition
rg -A 5 "class ApiLocationJourney" 

Length of output: 462


Script:

#!/bin/bash
# Description: Get more context from the journey.dart file to verify type field definition

# Show more lines after the factory constructor to see all fields
rg -A 15 "const factory ApiLocationJourney" data/lib/api/location/journey/journey.dart

Length of output: 542

@cp-ishita-g cp-ishita-g merged commit 787c7b7 into main Dec 12, 2024
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants