Skip to content

feat: added csv functionalities in accelerometer screen. #2820

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

Merged
merged 6 commits into from
Aug 16, 2025

Conversation

Yugesh-Kumar-S
Copy link
Contributor

@Yugesh-Kumar-S Yugesh-Kumar-S commented Aug 7, 2025

Fixes #2819

Changes

  • added csv functionalities to accelerometer screen
  • adapted logged data chart screen to support gyroscope and accelerometer data.

Screenshots / Recordings

screen-20250807-194948.mp4

Checklist:

  • No hard coding: I have used resources from strings.xml, dimens.xml and colors.xml without hard coding any value.
  • No end of file edits: No modifications done at end of resource files strings.xml, dimens.xml or colors.xml.
  • Code reformatting: I have reformatted code and fixed indentation in every file included in this pull request.
  • No extra space: My code does not contain any extra lines or extra spaces than the ones that are necessary.

Summary by Sourcery

Enable CSV recording and export of accelerometer readings, integrate UI controls for recording and saving, and enhance logged-data screens and chart rendering for multi-axis instruments

New Features:

  • Add start/stop recording controls in the accelerometer screen to capture sensor data
  • Provide a file‐save dialog for naming and exporting recorded accelerometer data as CSV
  • Allow navigation from the accelerometer screen to the Logged Data screen to review saved recordings

Enhancements:

  • Adapt LoggedDataChartScreen and LoggedDataScreen to support both accelerometer and gyroscope data with an axis selector
  • Improve chart rendering with dynamic time‐based x-axis, safe y-axis scaling, and formatted time labels
  • Update AccelerometerStateProvider to timestamp and format each recorded data row
  • Simplify CsvService to use external storage directories without runtime permission prompts
  • Adjust default data‐column indices across logged data screens to align with the new CSV format

Copy link
Contributor

sourcery-ai bot commented Aug 7, 2025

Reviewer's Guide

Implements CSV-based data recording/export in AccelerometerScreen with UI dialogs and navigation, refactors the logged data chart for multi-axis sensor support and dynamic scaling, extends the accelerometer provider to manage recordings, updates CSV storage path logic, and adjusts navigation parameters for logged data display.

Sequence diagram for accelerometer data recording and CSV export

sequenceDiagram
    actor User
    participant AccelerometerScreen
    participant AccelerometerStateProvider
    participant CsvService
    User->>AccelerometerScreen: Tap record button
    AccelerometerScreen->>AccelerometerStateProvider: startRecording()
    AccelerometerStateProvider-->>AccelerometerScreen: isRecording = true
    AccelerometerStateProvider->>AccelerometerStateProvider: Collects sensor data
    User->>AccelerometerScreen: Tap stop record button
    AccelerometerScreen->>AccelerometerStateProvider: stopRecording()
    AccelerometerStateProvider-->>AccelerometerScreen: Returns recorded data
    AccelerometerScreen->>User: Show save file dialog
    User->>AccelerometerScreen: Enter file name and confirm
    AccelerometerScreen->>CsvService: saveCsvFile('accelerometer', fileName, data)
    CsvService-->>AccelerometerScreen: File saved (or error)
    AccelerometerScreen->>User: Show success/failure message
Loading

Entity relationship diagram for recorded accelerometer CSV data

erDiagram
    ACCELEROMETER_RECORDINGS {
      string Timestamp
      string DateTime
      double ReadingsX
      double ReadingsY
      double ReadingsZ
      double Latitude
      double Longitude
    }
    ACCELEROMETER_RECORDINGS ||..|| CSV_FILE : exported_to
Loading

Class diagram for updated AccelerometerStateProvider and CSV integration

classDiagram
    class AccelerometerStateProvider {
      - AccelerometerEvent _accelerometerEvent
      - StreamSubscription _accelerometerSubscription
      - List<double> _xData
      - List<double> _yData
      - List<double> _zData
      - double _xMin
      - double _xMax
      - double _yMin
      - double _yMax
      - double _zMin
      - double _zMax
      - bool _isRecording
      - List<List<dynamic>> _recordedData
      + bool isRecording
      + void initializeSensors()
      + void disposeSensors()
      + void startRecording()
      + List<List<dynamic>> stopRecording()
      + List<FlSpot> getAxisData(String axis)
    }
    class CsvService {
      + Future<Directory> getInstrumentDirectory(String instrumentName)
      + Future<File?> saveCsvFile(String instrumentName, String fileName, List<List<dynamic>> data)
      + void writeMetaData(String instrumentName, List<List<dynamic>> data)
    }
    AccelerometerStateProvider -- CsvService : uses
Loading

Class diagram for updated LoggedDataChartScreen multi-axis support

classDiagram
    class LoggedDataChartScreen {
      + String fileName
      + List<List<dynamic>> data
      + String yAxisLabel
      + int xDataColumnIndex
      + int yDataColumnIndex
      + String? instrumentName
      - String selectedAxis
      - bool get _shouldShowAxisSelector
      - int _getYDataColumnIndex()
      - Widget _buildChart(...)
      - Widget _sideTitleWidgets(...)
    }
    LoggedDataChartScreen <|-- StatefulWidget
    LoggedDataChartScreen : +axis selector for accelerometer/gyroscope
    LoggedDataChartScreen : +dynamic scaling and axis selection
Loading

File-Level Changes

Change Details Files
Integrated CSV export and recording workflow in the accelerometer screen
  • Imported CsvService and instantiated it in the State
  • Initialized and disposed AccelerometerStateProvider manually in initState/dispose
  • Added _toggleRecording to start/stop data capture with snackbars
  • Implemented _showSaveFileDialog with filename input and CsvService calls
  • Wired record button and isRecording state into CommonScaffold
  • Added navigation to LoggedDataScreen via _navigateToLoggedData
lib/view/accelerometer_screen.dart
Enhanced logged data chart to support multi-axis sensor data
  • Added instrumentName parameter and propagated it from LoggedDataScreen
  • Introduced axis selector dropdown in AppBar for x/y/z channels
  • Computed yDataColumnIndex dynamically based on selected axis
  • Converted absolute timestamps to relative seconds in data points
  • Calculated dynamic min/max Y bounds with padding and y-axis intervals
  • Refactored _buildChart signature and extracted _sideTitleWidgets for time formatting
lib/view/logged_data_chart_screen.dart
Extended accelerometer state provider to capture and manage recordings
  • Added isRecording flag and recordedData list
  • Implemented startRecording to initialize CSV headers
  • Implemented stopRecording to return captured data
  • Appended timestamped sensor readings inside sensor event listener
lib/providers/accelerometer_state_provider.dart
Updated CSV service storage logic
  • Replaced manual storage permission requests and hardcoded path
  • Switched to getExternalStorageDirectory for building instrument folder path
  • Removed permission_handler dependency and related code
lib/others/csv_service.dart
Adjusted navigation parameters for logged data screens
  • Passed instrumentName into LoggedDataChartScreen builder
  • Changed default xDataColumnIndex from 1 to 0 for all instruments
lib/view/logged_data_screen.dart

Assessment against linked issues

Issue Objective Addressed Explanation
#2819 Add CSV functionalities in accelerometer instrument using the csv_service.dart.
#2819 Adapt the logged_data_chart_screen.dart to display accelerometer data.

Possibly linked issues


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
Contributor

@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 @Yugesh-Kumar-S - I've reviewed your changes - here's some feedback:

  • Consider using Provider’s create constructor instead of manually instantiating and disposing AccelerometerStateProvider in your State; that will simplify lifecycle management and avoid potential double-dispose issues.
  • The CSVService currently writes to external storage without guarding for platform differences or null paths—add proper permission checks and handle cases where getExternalStorageDirectory() might return null.
  • The chart range, padding, and interval calculations are fairly complex and partially duplicated—extract them into utility methods to make the rendering logic more concise and maintainable.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider using Provider’s `create` constructor instead of manually instantiating and disposing `AccelerometerStateProvider` in your `State`; that will simplify lifecycle management and avoid potential double-dispose issues.
- The CSVService currently writes to external storage without guarding for platform differences or null paths—add proper permission checks and handle cases where `getExternalStorageDirectory()` might return null.
- The chart range, padding, and interval calculations are fairly complex and partially duplicated—extract them into utility methods to make the rendering logic more concise and maintainable.

## Individual Comments

### Comment 1
<location> `lib/view/accelerometer_screen.dart:160` </location>
<code_context>
+  Future<void> _showSaveFileDialog(List<List<dynamic>> data) async {
</code_context>

<issue_to_address>
writeMetaData is called before saveCsvFile, but its result is not checked.

If writeMetaData can fail, handle its result or exceptions, or document why errors are ignored.

Suggested implementation:

```
  Future<void> _showSaveFileDialog(List<List<dynamic>> data) async {
    final TextEditingController filenameController = TextEditingController();
    final String defaultFilename = '';
    filenameController.text = defaultFilename;

    final String? fileName = await showDialog<String>(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: Text(appLocalizations.saveRecording),
          content: TextField(

```

```
    // Example: handle writeMetaData result before saving CSV file
    // (Assuming writeMetaData returns a bool or throws on error)
    Future<bool> writeMetaDataSafe() async {
      try {
        final result = await writeMetaData();
        if (result == false) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(
              content: Text(appLocalizations.metaDataWriteFailed),
              backgroundColor: Colors.red,
            ),
          );
          return false;
        }
        return true;
      } catch (e) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('${appLocalizations.metaDataWriteFailed}: $e'),
            backgroundColor: Colors.red,
          ),
        );
        return false;
      }
    }

    // Usage example:
    // if (await writeMetaDataSafe()) {
    //   await saveCsvFile(data);
    // }
  }

```

- Replace the call to `writeMetaData` in your save logic with `await writeMetaDataSafe()`, and only proceed to `saveCsvFile` if it returns true.
- If `writeMetaData` has a different signature or error handling, adjust the helper accordingly.
- Add a localized string for `metaDataWriteFailed` if it does not exist.
</issue_to_address>

### Comment 2
<location> `lib/providers/accelerometer_state_provider.dart:103` </location>
<code_context>
   }

+  void startRecording() {
+    _isRecording = true;
+    _recordedData = [
+      [
+        'Timestamp',
+        'DateTime',
+        'ReadingsX',
+        'ReadingsY',
+        'ReadingsZ',
+        'Latitude',
+        'Longitude'
+      ]
+    ];
+    notifyListeners();
+  }
+
</code_context>

<issue_to_address>
Recording state is reset on every startRecording call.

If startRecording is called while already recording, existing data will be overwritten. Add a check to prevent this or document the intended behavior.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
  void startRecording() {
    _isRecording = true;
    _recordedData = [
      [
        'Timestamp',
        'DateTime',
        'ReadingsX',
        'ReadingsY',
        'ReadingsZ',
        'Latitude',
        'Longitude'
      ]
    ];
    notifyListeners();
  }
=======
  /// Starts recording accelerometer data.
  /// If already recording, this method does nothing.
  void startRecording() {
    if (_isRecording) {
      // Already recording; do not reset data.
      return;
    }
    _isRecording = true;
    _recordedData = [
      [
        'Timestamp',
        'DateTime',
        'ReadingsX',
        'ReadingsY',
        'ReadingsZ',
        'Latitude',
        'Longitude'
      ]
    ];
    notifyListeners();
  }
>>>>>>> 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.

Copy link

github-actions bot commented Aug 7, 2025

@marcnause marcnause merged commit a79ee67 into fossasia:flutter Aug 16, 2025
7 of 8 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.

add csv functionalities to accelerometer.
2 participants