Skip to content

Conversation

@Yugesh-Kumar-S
Copy link
Contributor

@Yugesh-Kumar-S Yugesh-Kumar-S commented Jun 4, 2025

Fixes #2715

Changes

  • Ported the Gyroscope Instrument Screen
  • Implemented Gyroscope with same view as Accelerometer to maintain code consistency

Screenshots / Recordings

Screenshot_20250604-212659

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

Implement a dedicated gyroscope instrument screen mirroring the existing accelerometer view to display real-time X, Y, and Z rotational data.

New Features:

  • Add GyroscopeScreen with a MultiProvider setup for gyroscope data streaming.
  • Implement GyroscopeProvider to subscribe to sensor events and maintain rolling window data for each axis.
  • Create GyroscopeCard widget displaying live gyroscope charts with current, min, and max readings for each axis.

Enhancements:

  • Register "/gyroscope" route and integrate navigation logic within the instruments menu.

Chores:

  • Define gyroscope titles, axis labels, and no-data message in constants.dart.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jun 4, 2025

Reviewer's Guide

This PR completes the Gyroscope feature by mirroring the existing Accelerometer implementation: it adds navigation support, string constants, a dedicated provider, chart widget, and a screen tied together with routing.

Sequence Diagram: Navigating to Gyroscope Screen

sequenceDiagram
    participant U as User
    participant IS as InstrumentsScreen
    participant Nav as Navigator
    participant GS as GyroscopeScreen

    U->>IS: Selects Gyroscope (item 10)
    IS->>Nav: pushNamedAndRemoveUntil("/gyroscope", (route) => route.isFirst)
    Nav->>GS: Creates and displays GyroscopeScreen
Loading

Sequence Diagram: Gyroscope Data Flow

sequenceDiagram
    participant GScreen as GyroscopeScreen
    participant GP as GyroscopeProvider
    participant Sensor as GyroscopeSensor (sensors_plus)
    participant GCard as GyroscopeCard

    GScreen->>GP: Create / Initialize (GyroscopeProvider()..initializeSensors())
    activate GP
    GP->>Sensor: gyroscopeEventStream().listen()
    deactivate GP

    loop Data Stream
        Sensor-->>GP: GyroscopeEvent
        activate GP
        GP->>GP: _updateData()
        GP->>GScreen: notifyListeners()  // Triggers UI update
        deactivate GP
    end

    GScreen->>GScreen: Rebuild UI
    GScreen->>GCard: Update with new data from GyroscopeProvider
    GCard->>GCard: Display data in chart
Loading

Class Diagram: New Gyroscope Components

classDiagram
    class GyroscopeScreen {
      +build(BuildContext context) Widget
    }
    class _GyroscopeScreenState {
      +build(BuildContext context) Widget
    }
    class GyroscopeCard {
      +String axis
      +Color color
      +createState() State
    }
    class _GyroscopeCardState {
      +build(BuildContext context) Widget
      +sideTitleWidgets(double value, TitleMeta meta) Widget
    }
    class GyroscopeProvider {
      <<ChangeNotifier>>
      -StreamSubscription_GyroscopeEvent_ _gyroscopeSubscription
      -GyroscopeEvent _gyroscopeEvent
      -List_double_ _xData
      -List_double_ _yData
      -List_double_ _zData
      +List_FlSpot_ xData
      +List_FlSpot_ yData
      +List_FlSpot_ zData
      +double xValue
      +double yValue
      +double zValue
      +double xMin
      +double xMax
      +double yMin
      +double yMax
      +double zMin
      +double zMax
      +bool isListening
      +initializeSensors() void
      +disposeSensors() void
      -_updateData() void
      +getAxisData(String axis) List_FlSpot_
      +getMin(String axis) double
      +getMax(String axis) double
      +getCurrent(String axis) double
      +getDataLength(String axis) int
      +dispose() void
    }
    class GyroscopeEvent {
      <<external: sensors_plus>>
      +double x
      +double y
      +double z
      +DateTime timestamp
    }
    class FlSpot{
      <<external: fl_chart>>
      +double x
      +double y
    }

    GyroscopeScreen "1" --o "1" _GyroscopeScreenState : creates
    _GyroscopeScreenState ..> GyroscopeProvider : uses (via Provider)
    _GyroscopeScreenState "1" *-- "3" GyroscopeCard : displays
    GyroscopeCard "1" --o "1" _GyroscopeCardState : creates
    _GyroscopeCardState ..> GyroscopeProvider : uses (via Provider)
    GyroscopeProvider ..> GyroscopeEvent : uses
    GyroscopeProvider ..> FlSpot : uses
Loading

File-Level Changes

Change Details Files
Added gyroscope navigation case in instruments screen
  • Introduced a case 10 branch to push or pop until /gyroscope
  • Used Navigator.pushNamedAndRemoveUntil to ensure clean navigation stack
lib/view/instruments_screen.dart
Defined gyroscope UI strings in constants
  • Added gyroscopeTitle and gyroscopeAxisLabel
  • Added fallback noData string
lib/constants.dart
Registered gyroscope route in app entrypoint
  • Imported gyroscope_screen.dart
  • Added /gyroscope to routes map
lib/main.dart
Created GyroscopeCard widget for axis charts
  • Built a card with icon, current/min/max labels, and line chart
  • Handled missing axis images with fallback icons
lib/view/widgets/gyroscope_card.dart
Implemented GyroscopeProvider for sensor data
  • Subscribed to gyroscopeEventStream and buffered last 50 readings
  • Computed min/max/current values and exposed them via getters
lib/providers/gyroscope_state_provider.dart
Built GyroscopeScreen tying widget and provider
  • Used MultiProvider to initialize GyroscopeProvider
  • Placed three GyroscopeCard widgets within CommonScaffold
lib/view/gyroscope_screen.dart

Assessment against linked issues

Issue Objective Addressed Explanation
#2715 Maintain consistency with the existing PSLab Android app's Gyroscope behavior and visual design.
#2715 Display real-time gyroscope data with separate charts for each axis (X, Y, Z) using color-coded visualization.
#2715 Integrate continuous sensor data streaming with proper scaling (-20 to +20 rad/s⁻¹) and value tracking.

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

@Yugesh-Kumar-S Yugesh-Kumar-S changed the title Final implementation of Gyroscope feat: Implemented Gyroscope Jun 4, 2025
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:

  • Eliminate the redundant notifyListeners() call since it’s already being invoked in _updateData(), to avoid double rebuilds.
  • Fix the typo in constants.dart ('No data availabl' should read 'No data available').
  • Initialize your min/max trackers (e.g. with ±double.infinity or the first data point) instead of zero so the computed minima/maxima are accurate from the start.
Here's what I looked at during the review
  • 🟡 General issues: 6 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Documentation: all looks good

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 +44 to +45
_updateData();
notifyListeners();
Copy link
Contributor

Choose a reason for hiding this comment

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

issue: Duplicate notifyListeners call

Consider removing one of the notifyListeners calls to prevent unnecessary rebuilds.

Comment on lines +38 to +42
double currVal = provider.getCurrent(widget.axis.toLowerCase());
double minVal = provider.getMin(widget.axis.toLowerCase());
double maxVal = provider.getMax(widget.axis.toLowerCase());
int dataLength = provider.getDataLength(widget.axis.toLowerCase());

Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Use dynamic Y-axis bounds

Hard-coding these values risks clipping actual data. Compute chartMinY and chartMaxY dynamically from the provider's min and max, adding appropriate padding.

Suggested change
double currVal = provider.getCurrent(widget.axis.toLowerCase());
double minVal = provider.getMin(widget.axis.toLowerCase());
double maxVal = provider.getMax(widget.axis.toLowerCase());
int dataLength = provider.getDataLength(widget.axis.toLowerCase());
double currVal = provider.getCurrent(widget.axis.toLowerCase());
double minVal = provider.getMin(widget.axis.toLowerCase());
double maxVal = provider.getMax(widget.axis.toLowerCase());
int dataLength = provider.getDataLength(widget.axis.toLowerCase());
// Compute dynamic Y-axis bounds with padding
double yPadding = (maxVal - minVal).abs() * 0.1;
if (yPadding == 0) {
yPadding = 1.0; // fallback padding if min and max are equal
}
double chartMinY = minVal - yPadding;
double chartMaxY = maxVal + yPadding;

Widget build(BuildContext context) {
GyroscopeProvider provider = Provider.of<GyroscopeProvider>(context);

List<FlSpot> spots = provider.getAxisData(widget.axis.toLowerCase());
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Handle empty data scenario in UI

Consider displaying a placeholder or the noData constant when spots is empty to ensure proper chart rendering.

void initializeSensors() {
if (_gyroscopeSubscription != null) return;

_gyroscopeSubscription = gyroscopeEventStream().listen(
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (performance): Throttle sensor update frequency

Consider throttling or sampling the sensor event stream to reduce unnecessary listener rebuilds and improve performance.

Suggested implementation:

  // Add import at the top of the file:
  // import 'package:rxdart/rxdart.dart';

  void initializeSensors() {
    if (_gyroscopeSubscription != null) return;

    _gyroscopeSubscription = gyroscopeEventStream()
        .throttleTime(const Duration(milliseconds: 50))
        .listen(
      (event) {
        _gyroscopeEvent = event;
        _updateData();
        notifyListeners();
      },
      onError: (error) {
        logger.e("Gyroscope error: $error");
      },
      cancelOnError: true,
    );
  }

  • Add import 'package:rxdart/rxdart.dart'; at the top of the file if not already present.
  • Ensure rxdart is included in your pubspec.yaml dependencies.
  • Adjust the throttle duration as needed for your use case.

@github-actions
Copy link
Contributor

github-actions bot commented Jun 4, 2025

@CloudyPadmal
Copy link
Collaborator

@Yugesh-Kumar-S do you think it is wise to consider the suggestions by Sourcery?

@Yugesh-Kumar-S
Copy link
Contributor Author

@Yugesh-Kumar-S do you think it is wise to consider the suggestions by Sourcery?

Yes sir , some of the Sourcery suggestions do improve the performance . To maintain code consistency i have implemented it similar to the Accelerometer , but the suggestions can also be included.

@AsCress
Copy link
Collaborator

AsCress commented Jun 5, 2025

@CloudyPadmal Seems mostly nice to me. The performance is good, I've tested on both Android and iOS.

Copy link
Collaborator

@AsCress AsCress left a comment

Choose a reason for hiding this comment

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

@CloudyPadmal CloudyPadmal merged commit da2abd0 into fossasia:flutter Jun 5, 2025
5 checks passed
Yugesh-Kumar-S added a commit to Yugesh-Kumar-S/pslab-android that referenced this pull request Jun 7, 2025
# This is the 1st commit message:

Reimplemented a simple themeing

# This is the commit message #2:

#feat: Implemented Gyroscope (fossasia#2723)

#resolved conflicts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gyroscope data visualization and analytics

3 participants