Flutter - WebRTC: not working on WIFI / works on Mobile Data - flutter

I am using Flutter WebRTC for creating P2P video calling.
I have encountered a problem which is related to networking: I have completeled the application but it only works with Mobile Data.
When changing the network to WiFi, it is not working and connection state hangs on Checking
I used Google Community STUN/TURN Servers and Node JS socket.io for signalling purposes. It also works when the mobile is not on same network but only with Mobile Data.

_createPeer() async {
try {
if (_peerConnection != null) return;
navigator.getUserMedia(mediaConstraints).then((stream) {
_localStream = stream;
_localRenderer.srcObject = stream;
});
_peerConnection = await createPeerConnection(configuration, constraints);
_peerConnection.onSignalingState = _onSignalingState;
_peerConnection.onIceGatheringState = _onIceGatheringState;
_peerConnection.onIceConnectionState = _onIceConnectionState;
_peerConnection.onAddStream = _onAddStream;
_peerConnection.onRemoveStream = _onRemoveStream;
_peerConnection.onIceCandidate = _onCandidate;
_peerConnection.onRenegotiationNeeded = _onRenegotiationNeeded;
_peerConnection.addStream(_localStream);
RTCSessionDescription offer =
await _peerConnection.createOffer(_offer_constraints);
_peerConnection.setLocalDescription(offer);
socket.emit('add-student', [
{'room': room, 'offer': offer.sdp}
]);
} catch (e) {
_snackBar(e.toString());
}
}
_onSignalingState(RTCSignalingState state) {
// _snackBar(state.toString());
}
_onIceGatheringState(RTCIceGatheringState state) {
// _snackBar(state.toString());
}
_onIceConnectionState(RTCIceConnectionState state) {
_snackBar(state.toString());
}
_onAddStream(MediaStream stream) {
if (stream == null) {
_snackBar('null');
return;
}
_progressVisible = false;
_buttonsVisible = true;
_remoteRenderer.srcObject = stream;
setState(() {});
}
_onRemoveStream(MediaStream stream) {
_snackBar('remove');
}
_onCandidate(RTCIceCandidate candidate) {
socket.emit('studentCandidate', [
{
'room': room,
'candidate': {
'candidate': candidate.candidate,
'sdpMid': candidate.sdpMid,
'sdpMLineIndex': candidate.sdpMlineIndex
}
}
]);
}
_onRenegotiationNeeded() {
_snackBar('reneg');
}

Related

Flutter Reactive BLE - How Do I Utilize Existing BLE Connection and the Existing Event Listener on Page Change?

I am writing an app in Flutter that uses flutter_reactive_ble 5.0.2. The app allows the user to connect to a BLE device via their phone. Once connected, the app sends a sequences of commands to the device that allows the device to connect to a WiFi network. Everything is working great in my initial main.dart page, but I'd like to send the user to a new page where I can generate a list view of available WiFi networks to pick from. How do I use the existing BLE connection and its associated 'event listener' in the new page?
My BLE connection is currently managed by the following code:
void onConnectDevice(index) async {
deviceIndex = index;
await scanStream.cancel();
currentConnectionStream = flutterReactiveBle.connectToAdvertisingDevice(
id: foundBleUARTDevices[index].id,
withServices: [_UART_UUID, _UART_RX, _UART_TX],
prescanDuration: const Duration(seconds: 5),
connectionTimeout: const Duration(seconds: 90),
);
statusUpdates = '';
refreshScreen();
connection = currentConnectionStream.listen((event) {
var id = event.deviceId.toString();
switch (event.connectionState) {
case DeviceConnectionState.connecting:
{
statusUpdates = "${statusUpdates}Connecting to $id\n";
break;
}
case DeviceConnectionState.connected:
{
connected = true;
statusUpdates = "${statusUpdates}Connected to $id\n";
numberOfMessagesReceived = 0;
receivedData = [];
txCharacteristic = QualifiedCharacteristic(
serviceId: _UART_UUID,
characteristicId: _UART_TX,
deviceId: event.deviceId,
);
receivedDataStream =
flutterReactiveBle.subscribeToCharacteristic(txCharacteristic);
receivedDataStream.listen((data) {
onNewReceivedData(data);
}, onError: (dynamic error) {
statusUpdates = "${statusUpdates}Error:$error$id\n";
});
rxCharacteristic = QualifiedCharacteristic(
serviceId: _UART_UUID,
characteristicId: _UART_RX,
deviceId: event.deviceId,
);
bleConnectionMessage = 'BLE connected';
break;
}
case DeviceConnectionState.disconnecting:
{
connected = false;
statusUpdates = "${statusUpdates}Disconnecting from $id\n";
break;
}
case DeviceConnectionState.disconnected:
{
statusUpdates = "${statusUpdates}Disconnected from $id\n";
break;
}
}
refreshScreen();
});
}
onNewReceivedData is listening to messages (command responses) coming over the UART.
Can anyone tell me how to do this?

PWA problem with Vue3+service worker+keep-alive

I have a problem with Vue3+service worker+keep-alive.
I use keep-live in template
<q-page-container>
<router-view v-slot="{ Component }">
<keep-alive :include="['WorkPage']">
<component :is="Component" :key="$route.fullPath"/>
</keep-alive>
</router-view>
</q-page-container>
create queue
createWorkQueue = new Queue('createWorkQueue', {
onSync: async ( {queue} ) => {
let entry
while (entry = await queue.shiftRequest()) {
try {
await fetch(entry.request);
const channel = new BroadcastChannel('sw-messages-work');
channel.postMessage({msg: 'offline-work-uploaded'});
} catch (error) {
await queue.unshiftRequest(entry);
throw error;
}
}
}
})
addEventListener('fetch'
self.addEventListener('fetch', (event) => {
if (event.request.url.endsWith('/api/ins_new_work')) {
const bgSyncLogic = async () => {
try {
const response = await fetch(event.request.clone())
return response
} catch (error) {
await createWorkQueue.pushRequest({request: event.request})
return error
}
}
event.respondWith(bgSyncLogic())
}
})
when in offline I send form - createWorkQueue.pushRequest hangs to 5 minutes
if I delete from keep-alive - WorkPage - then pushRequest works well
but I need keep-alive page. How can I solve this?
I found!!
I use IndexedDB library and for show offline message I read from DB information
const db = await openDB('workbox-background-sync')
but in first time - table 'requests' don't create
I insert next code
const db = await openDB('workbox-background-sync', undefined, { upgrade(db) { db.createObjectStore('requests') }})
and works well

SSE "data" field not being received by Dart http.client

I'm building a Flutter app that receives SSE from a server and translates them to specific notifications. The server is a Spring Boot app returning events containing "event:" and "data:" fields:
public void pushNotification(String username, PushEvent event) {
var emitter = emitters.get(username);
if (emitter == null) {
return;
}
try {
emitter.send(event.toSseEvent());
} catch (IOException e) {
log.debug("Could not send event for user " + username);
emitters.remove(username);
}
}
public class PushEvent {
private String type;
private Map<String, Object> body;
public SseEmitter.SseEventBuilder toSseEvent() {
return SseEmitter.event().name(type).data(body);
}
}
On the Flutter app, I use the Dart http package to open a Stream and receive the events:
Future<void> subscribe() async {
if (!_userModel.hasAuthentication()) {
return;
}
var user = _userModel.user as AuthenticatedUser;
var username = user.username;
var token = _userModel.getToken();
var uri = Uri.https(ApiUtils.API_BASE, '/api/push/subscribe/$username');
try {
var client = http.Client();
_client = client;
var request = new http.Request("GET", uri);
request.headers["Accept"] = "text/event-stream";
request.headers["Cache-Control"] = "no-cache";
request.headers["Authorization"] = token;
var response = await client.send(request);
if (response.statusCode == 200) {
_isSubscribed = true;
response.stream.toStringStream().forEach((value) {
var event = ServerEvent.parse(value);
_handleEvents(event);
}).onError((error, stackTrace) {
log.info("Connection closed");
log.info(error);
log.info(stackTrace);
unsubscribe();
}).whenComplete(() {
log.info("Connection completed");
unsubscribe();
subscribe();
});
} else {
_isSubscribed = false;
}
notifyListeners();
} catch (e) {
unsubscribe();
log.warning("Could not subscribe to notifications");
log.warning(e);
}
}
However, when I receive an event containing data from the server, the data does not show on the log:
I/flutter (14779): event:FRIEND_REQUEST
I/flutter (14779): data:
I am certain the data is being sent by the server since the React app on the same domain decodes the SSE and shows the notifications as intended:
const subscribePush = () => {
const username = sessionStorage.getItem('loggedUsername');
const token = sessionStorage.getItem('token');
var es = new EventSourcePolyfill(
'/api/push/subscribe/' + username,
{
headers: {
"Authorization": token,
}
}
);
es.onerror = () => es.close();
es.addEventListener("FRIEND_REQUEST", e => handleFriendRequestEvent(e));
es.addEventListener("FRIEND_ACCEPT", e => handleFriendAcceptEvent(e));
}
const handleFriendRequestEvent = function (event) {
const username = sessionStorage.getItem("loggedUsername");
const data = JSON.parse(event.data);
const source = data.source;
if (source !== username) {
var note = `${source} solicitou sua amizade!`;
var newNotifs = notifications.concat(note);
setNotifications(newNotifs);
setNewNotifications(newNotifications + 1);
}
}
Could something be missing from the request on the Flutter app, or is it possibly a bug?
Your implementation looks strangely similar to this one:
https://github.com/stevenroose/dart-eventsource
Take a look at the client implementation and how the response in decoded using the decoder.dart file.

Dart - How can I handle multiple socket responses?

I need to constantly query to a server socket four commands stored in the List pollingCommands:
Command1, Command2, Command3 and Command4 which returns Answer1, Answer2, Answer3 and Answer4 respectively.
My approach has been the following:
Socket.connect(ip, port).then((Socket sock) {
_socket = sock;
_socket.listen(
dataHandler,
onError: errorHandler,
onDone: doneHandler,
cancelOnError: false,
);
timer = Timer.periodic(Duration(milliseconds: 100), (timer) {
if (isComplete) {
_socket.write(
Command.pollingQuery(pollingCommands[pollingCycle]),
);
}
});
}).catchError((Object error) {
print("ERROR !");
});
This is the data handler
void dataHandler(data) {
result = String.fromCharCodes(data).trim();
if (isComplete) {
isComplete = false;
//Some action
if (pollingCycle == pollingCommands.length - 1) {
pollingCycle = 0;
isComplete = true;
} else {
pollingCycle++;
isComplete = true;
}
}
}
This approach is working for me if the timer is greater than 500ms but im not geting it to work with higher frequency as the commands does not correspond to their answer.
Any help? Thanks

Phonegap AdMob does not work on iPhone

My problem is that admob plugin is not working on the PhoneGap IOS testing app (this is the plugin: https://github.com/appfeel/admob-google-cordova)
var isAppForeground = true;
function initAds() {
if (!admob) {
var adPublisherIds = {
ios : {
banner : "ca-app-pub-3940256099942544/2934735716",
interstitial : "ca-app-pub-3940256099942544/4411468910"
},
android : {
banner : "ca-app-pub-3940256099942544/6300978111",
interstitial : "ca-app-pub-3940256099942544/1033173712"
}
};
var admobid = (/(android)/i.test(navigator.userAgent)) ? adPublisherIds.android : adPublisherIds.ios;
admob.setOptions({
publisherId: admobid.banner,
interstitialAdId: admobid.interstitial,
//tappxIdiOS: "/XXXXXXXXX/Pub-XXXX-iOS-IIII",
//tappxIdAndroid: "/XXXXXXXXX/Pub-XXXX-Android-AAAA",
//tappxShare: 0.5,
});
registerAdEvents();
} else {
alert('admobAds plugin not ready');
}
}
function onAdLoaded(e) {
if (isAppForeground) {
if (e.adType === admob.AD_TYPE.INTERSTITIAL) {
console.log("An interstitial has been loaded and autoshown. If you want to load the interstitial first and show it later, set 'autoShowInterstitial: false' in admob.setOptions() and call 'admob.showInterstitialAd();' here");
} else if (e.adType === admob.AD_TYPE_BANNER) {
console.log("New banner received");
}
}
}
function onPause() {
if (isAppForeground) {
admob.destroyBannerView();
isAppForeground = false;
}
}
function onResume() {
if (!isAppForeground) {
setTimeout(admob.createBannerView, 1);
setTimeout(admob.requestInterstitialAd, 1);
isAppForeground = true;
}
}
// optional, in case respond to events
function registerAdEvents() {
document.addEventListener(admob.events.onAdLoaded, onAdLoaded);
document.addEventListener(admob.events.onAdFailedToLoad, function (e) {});
document.addEventListener(admob.events.onAdOpened, function (e) {});
document.addEventListener(admob.events.onAdClosed, function (e) {});
document.addEventListener(admob.events.onAdLeftApplication, function (e) {});
document.addEventListener(admob.events.onInAppPurchaseRequested, function (e) {});
document.addEventListener("pause", onPause, false);
document.addEventListener("resume", onResume, false);
}
The plugin is successfully installed and I placed these 3 lines under onDeviceReady:
initAds();
admob.createBannerView();
admob.requestInterstitialAd();
Everything after the lines above, in the same function (onDeviceReady) is not executed (the function crashes).
Again, I use the PhoneGap app for testing (from AppStore).