-
Notifications
You must be signed in to change notification settings - Fork 7
Open
Description
Bridgefy messaging functionality is not working between iOS and Android devices, preventing cross-platform communication.
Expected Behavior
- Messages sent from iOS devices should be received by Android devices
- Messages sent from Android devices should be received by iOS devices
- Mesh networking should work seamlessly across both platforms
Actual Behavior
- Messages are not being transmitted/received between iOS and Android devices
- Cross-platform discovery and connection may be failing
- Communication seems to work within the same platform (iOS to iOS)
This is my service code, execute on both iOS, iPadOS and Android.
Messages from Android are not received on iOS and iPadOS, and messages from iOS are not received on Android.
I'm just using broadcast transmissionMode, no p2p connections.
I get no errors and no logs while sending or receiving. Bridgefy is successfully started on both devices
// ignore_for_file: constant_identifier_names
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:bridgefy/bridgefy.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:praisetune/config/env.dart';
import 'package:praisetune/extra/utility.dart';
class NearDevice {
String username;
String? deviceModel;
bool get following => NearDevice.followingList.contains(uuid);
bool blocked;
String uuid;
int? lastSongId;
String get iconLetter {
if (username.trim().isNotEmpty) {
return username.split(".").map((piece) => piece.substring(0, 1)).take(2).join().toUpperCase();
} else {
return "?";
}
}
static List<String> followingList = [];
//String get iconLetter => username.trim().isNotEmpty ? username.substring(0, 1).toUpperCase() : "?";
NearDevice({
required this.username,
required this.uuid,
this.blocked = false,
this.deviceModel,
});
}
extension MyNearDeviceListExtensions on List<NearDevice> {
NearDevice? findByUuid(String uuid) {
return firstWhereOrNull((device) => device.uuid == uuid);
}
}
final bridgefyServiceProvider = ChangeNotifierProvider<BridgefyService>((ref) {
return BridgefyService();
});
class BridgefyService extends ChangeNotifier implements BridgefyDelegate {
final _bridgefy = Bridgefy();
factory BridgefyService() => _instance;
BridgefyService._internal();
static final BridgefyService _instance = BridgefyService._internal();
static const int MSG_USER_NAME = 1;
static const int MSG_SONG_ID = 2;
static const int MSG_PING = 3;
static const int MSG_STOP = 4;
static const int MSG_TRANSPOSE = 5;
bool isInitialized = false;
bool isStarted = false;
bool permissionsGranted = false;
String userId = "";
String? username = "";
Timer? _debounceTransposeTimer;
final nearDevices = <NearDevice>[];
Future<bool> _checkPermissions() async {
final status = await [
Permission.location,
Permission.bluetoothAdvertise,
Permission.bluetoothConnect,
Permission.bluetoothScan,
].request();
if (Platform.isIOS) return true;
bool granted = true;
status.forEach((key, value) {
if (value == PermissionStatus.permanentlyDenied || value == PermissionStatus.denied) {
granted = false;
}
});
if (!granted) {
openAppSettings();
}
return granted;
}
Future<void> initialized() async {
myPrint("Initializing Bridgefy");
try {
permissionsGranted = await _checkPermissions();
if (!permissionsGranted || isInitialized) return;
await _bridgefy.initialize(
apiKey: Environment().config.bridgefyApiKey,
delegate: this,
verboseLogging: true,
);
myPrint("Bridgefy initialized with API Key: ${Environment().config.bridgefyApiKey}");
isInitialized = await _bridgefy.isInitialized;
notifyListeners();
try {
// aggiorno la licenza sempre, però se non è connesso ad internet lascia stare
await _bridgefy.updateLicense();
} catch (err) {
myPrint("Non connesso ad internet, non aggiorno la licenza");
}
} catch (e) {
if (e is BridgefyError && e.type == BridgefyErrorType.alreadyInstantiated) {
isInitialized = await _bridgefy.isInitialized;
notifyListeners();
}
myPrint(e);
}
}
Future<void> start({
String? userId,
BridgefyPropagationProfile propagationProfile = BridgefyPropagationProfile.standard,
}) async {
if (!isInitialized || !permissionsGranted) {
await initialized();
}
if (!permissionsGranted) {
return;
}
if (!(await _bridgefy.isInitialized)) {
isInitialized = false;
await initialized();
}
assert(isInitialized, 'Bridgefy is not initialized');
await _bridgefy.start();
myPrint("Bridgefy partito! (in teoria)");
notifyListeners();
}
Future<void> stop() async {
assert(isInitialized, 'Bridgefy is not initialized');
assert(isStarted, 'Bridgefy is not started');
await _bridgefy.send(
data: Uint8List.fromList([MSG_STOP]),
transmissionMode: BridgefyTransmissionMode(type: BridgefyTransmissionModeType.broadcast, uuid: userId));
await _bridgefy.stop();
nearDevices.clear();
notifyListeners();
}
@override
void bridgefyDidConnect({required String userID}) {
// TODO: implement bridgefyDidConnect
}
@override
void bridgefyDidDestroySession() {
// TODO: implement bridgefyDidDestroySession
}
@override
void bridgefyDidDisconnect({required String userID}) {
// TODO: implement bridgefyDidDisconnect
}
@override
void bridgefyDidEstablishSecureConnection({required String userID}) {
// TODO: implement bridgefyDidEstablishSecureConnection
}
@override
void bridgefyDidFailSendingMessage({required String messageID, BridgefyError? error}) {
myPrint("Ah non ho proprio inviato..");
myPrint(error);
// TODO: implement bridgefyDidFailSendingMessage
}
Function(int, NearDevice)? onSongIdReceived;
Function(int, NearDevice)? onTransposeReceived;
void Function()? onBridgefyStart;
@override
void bridgefyDidFailToDestroySession() {
// TODO: implement bridgefyDidFailToDestroySession
}
@override
void bridgefyDidFailToEstablishSecureConnection({required String userID, required BridgefyError error}) {
// TODO: implement bridgefyDidFailToEstablishSecureConnection
}
@override
void bridgefyDidFailToStart({required BridgefyError error}) {
// TODO: implement bridgefyDidFailToStart
}
@override
void bridgefyDidFailToStop({required BridgefyError error}) {
// TODO: implement bridgefyDidFailToStop
}
@override
void bridgefyDidReceiveData(
{required Uint8List data, required String messageId, required BridgefyTransmissionMode transmissionMode}) {
myPrint("Ho ricevuto qualcosa!");
final type = data[0];
myPrint(type);
if (type == BridgefyService.MSG_SONG_ID) {
// TODO: ora che ci penso, con la sessione, uno potrebbe avere il permesso di aprire canzoni non sue, senza poterle salvare ovviamente.
final byteData = ByteData.sublistView(data);
final songId = byteData.getUint32(1);
final device = nearDevices.findByUuid(transmissionMode.uuid);
if (device != null && device.following) {
device.lastSongId = songId;
onSongIdReceived?.call(songId, device);
}
}
if (type == BridgefyService.MSG_TRANSPOSE) {
final transpose = data[1];
final device = nearDevices.findByUuid(transmissionMode.uuid);
if (device != null && device.following) {
onTransposeReceived?.call(transpose - 12, device);
}
}
if (type == BridgefyService.MSG_USER_NAME) {
final name = utf8.decode(data.sublist(1)); // 1 after the MSG_USER_NAME
final existing = nearDevices.findByUuid(transmissionMode.uuid);
if (existing != null) {
if (existing.username != name) {
existing.username = name;
notifyListeners();
}
} else {
nearDevices.add(NearDevice(username: name, uuid: transmissionMode.uuid));
notifyListeners();
}
}
if (type == BridgefyService.MSG_PING) {
sendUsername();
}
if (type == BridgefyService.MSG_STOP) {
nearDevices.removeWhere((device) => device.uuid == transmissionMode.uuid);
notifyListeners();
}
}
@override
void bridgefyDidSendDataProgress({required String messageID, required int position, required int of}) {
// TODO: implement bridgefyDidSendDataProgress
myPrint("O almeno ci provo");
}
@override
void bridgefyDidSendMessage({required String messageID}) {
// TODO: implement bridgefyDidSendMessage
myPrint("Ho inviato un messaggio!");
}
@override
void bridgefyDidStart({required String currentUserID}) {
isStarted = true;
userId = currentUserID;
myPrint("Bridgefy partito! (in PRATICA)");
onBridgefyStart?.call();
notifyListeners();
}
@override
void bridgefyDidStop() {
isStarted = false;
userId = '';
notifyListeners();
}
Future<void> sendUsername() {
if (username == null || username!.isEmpty) throw "Utente non registrato";
if (!isStarted) throw BridgefyError(type: BridgefyErrorType.notStarted);
final nameBytes = Uint8List.fromList(username!.codeUnits); // UTF-16 bytes
final result = Uint8List.fromList([
MSG_USER_NAME,
...nameBytes,
]);
return _bridgefy.send(
data: result,
transmissionMode: BridgefyTransmissionMode(type: BridgefyTransmissionModeType.broadcast, uuid: userId));
}
Future<void> pingNearDevices() {
if (!isStarted) throw BridgefyError(type: BridgefyErrorType.notStarted);
myPrint("Pinging near devices!");
return _bridgefy.send(
data: Uint8List.fromList([MSG_PING]),
transmissionMode: BridgefyTransmissionMode(type: BridgefyTransmissionModeType.broadcast, uuid: userId));
}
Future<void> sendTranspose(int transpose, {bool noDebounce = false}) {
if (!isStarted) throw BridgefyError(type: BridgefyErrorType.notStarted);
if (noDebounce) {
return _bridgefy.send(
data: Uint8List.fromList([BridgefyService.MSG_TRANSPOSE, transpose + 12]),
transmissionMode: BridgefyTransmissionMode(type: BridgefyTransmissionModeType.broadcast, uuid: userId));
} else {
_debounceTransposeTimer?.cancel();
_debounceTransposeTimer = Timer(const Duration(seconds: 1), () {
sendTranspose(transpose, noDebounce: true);
});
return Future.value();
}
}
Future<void> sendSongId(int songId) {
if (!isStarted) throw BridgefyError(type: BridgefyErrorType.notStarted);
final bytes = ByteData(5);
bytes.setUint8(0, BridgefyService.MSG_SONG_ID);
bytes.setUint32(1, songId);
return _bridgefy.send(
data: bytes.buffer.asUint8List(),
transmissionMode: BridgefyTransmissionMode(type: BridgefyTransmissionModeType.broadcast, uuid: userId));
}
void followDevice(NearDevice device) {
//if (device.blocked) return;
if (!NearDevice.followingList.contains(device.uuid)) {
NearDevice.followingList.add(device.uuid);
}
notifyListeners();
}
void unfollowDevice(NearDevice device) {
NearDevice.followingList.remove(device.uuid);
notifyListeners();
}
}
What am I doing wrong?
Thanks...
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.manudicri.praisetune" xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"
android:maxSdkVersion="30"
tools:node="replace" />
<uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="32"
tools:node="replace" />
<application android:label="PraiseTune" android:icon="@mipmap/ic_launcher" android:usesCleartextTraffic="true"
android:enableOnBackInvokedCallback="true"
>
<meta-data android:name="com.bridgefy.sdk.API_KEY" android:value="<my_api_key>" />
<activity android:name=".MainActivity" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize" android:exported="true">
<meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme"/>
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- App Link sample -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="praisetune.com" android:pathPattern="/app/.*"/>
<data android:scheme="https" android:host="praisetune.com" android:pathPattern="/app/.*" />
<data android:scheme="http" android:host="praisetune.com" android:pathPattern="/songs/.*"/>
<data android:scheme="https" android:host="praisetune.com" android:pathPattern="/songs/.*" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data android:name="flutterEmbedding" android:value="2"/>
</application>
</manifest>flutter doctor -v
[✓] Flutter (Channel stable, 3.32.2, on macOS 15.4 24E248 darwin-arm64, locale it-IT) [599ms]
• Flutter version 3.32.2 on channel stable at /Users/manudicri/Development/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 8defaa71a7 (5 days ago), 2025-06-04 11:02:51 -0700
• Engine revision 1091508939
• Dart version 3.8.1
• DevTools version 2.45.1
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) [2,2s]
• Android SDK at /Users/manudicri/Library/Android/sdk
• Platform android-35, build-tools 34.0.0
• Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
This is the JDK bundled with the latest Android Studio installation on this machine.
To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
• Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 16.1) [1.456ms]
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 16B40
• CocoaPods version 1.16.2
[✓] Chrome - develop for the web [12ms]
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2022.2) [12ms]
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)
[✓] VS Code (version 1.100.3) [11ms]
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.112.0
[✓] Connected device (5 available) [7,7s]
• AGS2 W09 (mobile) • JALBB20C04101156 • android-arm64 • Android 8.0.0 (API 26)
• iPhone di Manuel (wireless) (mobile) • 00008110-00041D0034D9801E • ios • iOS 18.5 22F76
• iPad di Manuel (mobile) • 00008101-0002406A14C1A01E • ios • iOS 18.4.1 22E252
• macOS (desktop) • macos • darwin-arm64 • macOS 15.4 24E248 darwin-arm64
• Chrome (web) • chrome • web-javascript • Google Chrome 137.0.7151.69
! Error: Browsing on the local area network for Apple Watch di Manuel. Ensure the device is unlocked and discoverable via Bluetooth. (code -27)
! Error: Browsing on the local area network for iPad di Gabriel. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
The device must be opted into Developer Mode to connect wirelessly. (code -27)
[✓] Network resources [783ms]
• All expected network resources are available.
• No issues found!
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels