-
Notifications
You must be signed in to change notification settings - Fork 243
feat: added usb transfer support (Android only) #1473
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
base: development
Are you sure you want to change the base?
Conversation
Reviewer's GuideThis PR integrates Android-only USB CDC/ACM serial communication alongside existing BLE transfers by reusing the BadgeMagic data protocol (but with 64-byte chunks), adds a transfer method UI with a provider, updates Android manifest and resources for USB host support, and switches to a modern usb_serial SDK-34+ fork. Sequence diagram for USB transfer processsequenceDiagram
actor User
participant App
participant TransferProvider
participant UsbCdc
User->>App: Tap "Transfer" button
App->>TransferProvider: openTray()
User->>App: Select "USB" in tray
App->>TransferProvider: selectMethod(ConnectionType.usb)
App->>UsbCdc: openDevice()
UsbCdc-->>App: Device opened
App->>UsbCdc: write(data in 64-byte chunks)
UsbCdc-->>App: Write success/failure
App->>User: Show transfer result
Class diagram for new and updated USB transfer classesclassDiagram
class TransferProvider {
- ConnectionType? _selectedMethod
- bool _showTray
+ openTray()
+ closeTray()
+ selectMethod(ConnectionType)
+ reset()
+ selectedMethod
+ showTray
}
class PayloadBuilder {
- DataTransferManager manager
+ buildPayloads()
}
class UsbCdc {
- UsbPort? _port
+ openDevice()
+ write(List<int>)
+ close()
+ listDevices()
+ isFossasiaDeviceConnected()
}
class UsbWriteState {
- PayloadBuilder builder
+ processState()
}
class UsbScanState {
- PayloadBuilder builder
+ processState()
}
TransferProvider <|.. AnimationBadgeProvider
PayloadBuilder --> DataTransferManager
UsbWriteState --> PayloadBuilder
UsbScanState --> PayloadBuilder
UsbWriteState --> UsbCdc
UsbScanState --> UsbCdc
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this 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:
- The HomeScreen widget is becoming very large and complex with USB transfer logic and UI baked in—extract the transfer tray and handleTransfer flow into smaller widgets or a dedicated controller/service to simplify maintenance.
- The handleAnimationTransfer method in AnimationBadgeProvider now has a big BLE vs. USB conditional; refactor by pulling out the USB-specific and BLE-specific flows into separate helper methods to clean up the logic.
- TransferMethodTray references colorPrimaryDark which isn’t defined in constants.dart—either define this color or switch to an existing constant like colorPrimary to fix the undefined reference.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The HomeScreen widget is becoming very large and complex with USB transfer logic and UI baked in—extract the transfer tray and handleTransfer flow into smaller widgets or a dedicated controller/service to simplify maintenance.
- The handleAnimationTransfer method in AnimationBadgeProvider now has a big BLE vs. USB conditional; refactor by pulling out the USB-specific and BLE-specific flows into separate helper methods to clean up the logic.
- TransferMethodTray references colorPrimaryDark which isn’t defined in constants.dart—either define this color or switch to an existing constant like colorPrimary to fix the undefined reference.
## Individual Comments
### Comment 1
<location> `lib/view/homescreen.dart:622` </location>
<code_context>
+ ),
+ ),
+ // Transfer Method Tray Overlay
+ Consumer<TransferProvider>(
+ builder: (context, transferProvider, _) {
+ if (transferProvider.showTray) {
</code_context>
<issue_to_address>
**issue (bug_risk):** The overlay for the transfer tray may block all interactions, including the tray itself.
The overlay's full-screen coverage may block tray interactions. Adjust the Stack order or hitTestBehavior to keep the tray accessible.
</issue_to_address>
### Comment 2
<location> `lib/providers/animation_badge_provider.dart:285-294` </location>
<code_context>
+ if (connectionType == ConnectionType.bluetooth) {
</code_context>
<issue_to_address>
**suggestion (bug_risk):** The USB transfer path does not reset animation state for special animations.
The BLE path resets animation mode and index for certain animations, but the USB path does not. This may cause inconsistent UI states. Please align the USB logic with BLE for animation state resets.
</issue_to_address>
### Comment 3
<location> `lib/providers/animation_badge_provider.dart:335-344` </location>
<code_context>
+ Future<void> write(List<int> data) async {
+ if (_port == null) throw Exception("USB port not open");
+
+ try {
+ await _port!.write(Uint8List.fromList(data));
+ logger.d("USB chunk written: ${data.length} bytes");
+ } catch (e) {
+ logger.e("Failed to write USB chunk: $e");
+ rethrow;
</code_context>
<issue_to_address>
**suggestion:** Error handling for USB transfer is limited to logging.
Please add user-facing error notifications for USB transfer failures to match the BLE error handling approach.
</issue_to_address>
### Comment 4
<location> `lib/bademagic_module/usb/usb_cdc.dart:35-37` </location>
<code_context>
+ logger.d("Found FOSSASIA device: ${device.vid}:${device.pid}");
+
+ // ✅ ADD THIS BOOTLOADER DETECTION
+ if (device.vid == bootloaderVendorId && device.pid == bootloaderProductId) {
+ logger.e("Device is in bootloader mode - cannot transfer data");
+ throw Exception("Device is in bootloader mode. Please disconnect, then connect without holding any buttons.");
+ }
+
</code_context>
<issue_to_address>
**suggestion:** Throwing an exception for bootloader mode may not be user-friendly.
Display a user-facing message explaining the bootloader mode before throwing the exception to clarify why the transfer is blocked.
Suggested implementation:
```
if (device.vid == bootloaderVendorId && device.pid == bootloaderProductId) {
logger.e("Device is in bootloader mode - cannot transfer data");
// Show user-facing message before throwing exception
// NOTE: Replace `context` with your actual BuildContext variable if needed
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Device is in bootloader mode. Please disconnect, then connect without holding any buttons. Data transfer is blocked in bootloader mode.",
),
backgroundColor: Colors.red,
duration: Duration(seconds: 5),
),
);
throw Exception("Device is in bootloader mode. Please disconnect, then connect without holding any buttons.");
}
```
- You must ensure that a valid `BuildContext` named `context` is available at this point in your code. If this is not inside a widget or you do not have access to `context`, you will need to refactor the code to pass it in, or use another user-facing notification mechanism appropriate for your app.
- Import `package:flutter/material.dart` if not already imported.
- If you use another UI framework, replace the `ScaffoldMessenger`/`SnackBar` code with the appropriate user notification method.
</issue_to_address>
### Comment 5
<location> `lib/view/widgets/transfer_method_tray.dart:17` </location>
<code_context>
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ height: MediaQuery.of(context).size.height / 3.8,
+ decoration: BoxDecoration(
+ color: Theme.of(context).dialogBackgroundColor,
</code_context>
<issue_to_address>
**suggestion:** Tray height is hardcoded as a fraction of screen height.
This approach may cause layout issues on devices with atypical screen sizes. Consider setting min/max height constraints or making the tray height responsive to its content.
Suggested implementation:
```
return Container(
constraints: BoxConstraints(
minHeight: 200,
maxHeight: MediaQuery.of(context).size.height * 0.6,
),
decoration: BoxDecoration(
color: Theme.of(context).dialogBackgroundColor,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 8,
offset: const Offset(0, -2),
),
```
If the tray's child widgets are not already responsive to their content, consider wrapping the main content inside the tray with an `IntrinsicHeight` or using a `Column` with `MainAxisSize.min` to ensure the tray adapts to its content. Adjust the minHeight and maxHeight values as appropriate for your design.
</issue_to_address>
### Comment 6
<location> `lib/providers/transfer_provider.dart:22-14` </location>
<code_context>
+ notifyListeners();
+ }
+
+ void selectMethod(ConnectionType method) {
+ _selectedMethod = method;
+ _showTray = false;
+ notifyListeners();
+ }
+
</code_context>
<issue_to_address>
**suggestion:** Selecting a method closes the tray immediately.
If transfer initiation is delayed, users may not receive immediate feedback. Consider keeping the tray open until the transfer begins or add a loading indicator.
Suggested implementation:
```
ConnectionType? _selectedMethod;
bool _showTray = false;
bool _isLoading = false;
ConnectionType? get selectedMethod => _selectedMethod;
bool get showTray => _showTray;
bool get isLoading => _isLoading;
void openTray() {
_showTray = true;
notifyListeners();
```
```
void selectMethod(ConnectionType method) {
_selectedMethod = method;
notifyListeners();
}
void startTransfer() {
_isLoading = true;
_showTray = false;
notifyListeners();
}
```
You will need to update the UI code to:
- Call `startTransfer()` when the transfer is actually initiated.
- Show a loading indicator when `isLoading` is true.
- Ensure the tray remains open after method selection, and only closes when transfer starts.
</issue_to_address>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
for all other platforms, app will work, but when you press transfer via usb, it will say Platform unsupported. |
@tushar11kh Please address all the suggestions given by the sourcey AI, and implement wherever necessary. |
I believe I implemented all suggestions given by the sourcey AI. |
@mariobehling @samruddhi-Rahegaonkar |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tushar11kh Thanks! Please resolve conflicts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
USB transfer is Android-only, but the UI tray shows USB as an option on all platforms.
changes : Hide or disable the USB option for non-Android platforms (iOS, Web, macOS) to avoid user confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider internationalisation for new error strings.
Hey @tushar11kh, please remove the unrelated file changes like |
26d2b83
to
5ed638a
Compare
8e296d2
to
092ddb4
Compare
Build StatusBuild successful. APKs to test: https://github.com/fossasia/badgemagic-app/actions/runs/18233285928/artifacts/4179072041. Screenshots |
@mariobehling @samruddhi-Rahegaonkar @nope3472
|
1f6a915
to
9e3f079
Compare
9e3f079
to
29dc788
Compare
Fixes #986
Overview
This PR adds USB transfer capability to the Badge Magic app, allowing users to send animations and text to FOSSASIA badges via USB connection alongside the existing Bluetooth functionality.
What's Implemented
USB CDC/ACM Communication
badgemagic-firmware
reference.VID=0x10FC, PID=0x55E0
VID=0x0416, PID=0x5020
(taken frombadgemagic-rs
)User Interface
Android Integration
AndroidManifest.xml
.device_filter.xml
for FOSSASIA device detection.Package Dependency Update
usb_serial: ^0.3.0
(outdated, SDK 33)Technical Details
UsbScanState
andUsbWriteState
.Testing Needed
Since I don’t have access to physical hardware, I need help with testing:
How to Test
Screenshots / Recordings
WhatsApp.Video.2025-09-15.at.04.08.05.mp4
Note
This implements only data transfer, not firmware flashing.
Checklist:
constants.dart
without hard coding any value.Summary by Sourcery
Add USB transfer capability to the Badge Magic app by implementing USB CDC/ACM serial communication, a transfer method selection UI, and corresponding Android configuration and dependency updates
New Features:
Summary by Sourcery
Add USB transfer support on Android by implementing USB CDC/ACM communication, integrating it into the animation transfer flow, adding a transfer method UI and provider, and updating Android configurations and dependencies
New Features:
Build: