I have already try to understand the API doc, the articles about them, and this post: How do you create a Stream in Dart
I'm making a simple web app using WebSocket. Actually, it's working well, but I want add a feature (enjoy learn).
This is my class (can be optimized I guess)
library Ask;
import 'dart:html';
import 'dart:async';
import 'dart:convert';
class Ask {
final String addr;
String _protocol;
String _port;
WebSocket _ws;
bool openned;
Map<int, Completer> _completer_list = {};
int _counter = 0;
static final Map<String, Ask> _cache = <String, Ask>{};
factory Ask(String addr) {
if (_cache.containsKey(addr)) {
return _cache[addr];
} else {
final ask_server = new Ask._internal(addr);
_cache[addr] = ask_server;
return ask_server;
}
}
Ask._internal(this.addr);
Future<bool> open() {
if (openned)
return true;
_completer_list[0] = new Completer();
if (window.location.protocol == 'http:') {
_port = ':8080/ws';
_protocol = 'ws://';
} else {
_port = ':8443/ws';
_protocol = 'wss://';
}
_ws = new WebSocket(_protocol + addr + _port);
_ws.onOpen.listen((e) {
_get_data();
_get_close();
openned = true;
_completer_list[0].complete(true);
});
return _completer_list[0].future;
}
Future<String> send(Map data) {
bool check = false;
int id;
_completer_list.forEach((k, v) {
if (v.isCompleted) {
id = data['ws_id'] = k;
_completer_list[k] = new Completer();
_ws.send(JSON.encode(data));
check = true;
}
});
if (!check) {
_counter++;
id = data['ws_id'] = _counter;
_completer_list[id] = new Completer();
_ws.send(JSON.encode(data));
}
return _completer_list[id].future;
}
void _get_data() {
_ws.onMessage.listen((MessageEvent data) {
var response = JSON.decode(data.data);
_completer_list[response['ws_id']].complete(response);
});
}
void _get_close() {
_ws.onClose.listen((_) {
print('Server have been lost. Try to reconnect in 3 seconds.');
new Timer(new Duration(seconds: 3), () {
_ws = new WebSocket(_protocol + addr + _port);
_get_data();
_get_close();
_ws.onOpen.listen((e) => print('Server is alive again.'));
});
});
}
}
Example of use:
void showIndex() {
Element main = querySelector('main');
Ask connect = new Ask('127.0.0.1');
Map request = {};
request['index'] = true;
connect.open().then((_) {
connect.send(request).then((data) {
main.setInnerHtml(data['response']);
});
});
}
I would replace the then by a listen who will be canceled when the message will completed. By this way, I can add a progress bar, I think...
So my question, my send function can be a stream and keep my concept of one websocket for all ? (yes, if my function is used when a request is in progress, it's sent and if she's finish before the first, I recovered her properly. Thank you ws_id).
Thank you.
I think what you need is a StreamController
https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart-async.StreamController
Related
How can I await a stream (or any other event queue) multiple times?
I tried Stream.first & Stream.single, both doesn't work.
What I want to do:
//next is a fake member
Future<void> xxx() async {
final eventStream = foo();
await eventStream.next; // wait for first event
//do some work
await eventStream.next; // wait for second event
//do some other differnet work
await eventStream.next; // wait for 3rd event
// another differnet work
return;
}
equvalent to:
Future<void> xxx() async {
final eventStream = foo();
int i=0;
await for (final _ in eventStream){
if(i==0);//do some work
else if(i==1);//do some other differnet work
else if(i==2){;break;}//another differnet work
++i;
}
return;
}
Try StreamQueue from package:async.
var q = StreamQueue(eventStream);
var v1 = await q.next;
var v2 = await q.next;
var v3 = await q.next;
// other work.
await q.cancel(); // If not listened to completely yet.
I end up with a custom class
class CompleterQueue<T> {
final _buf = <Completer<T>>[];
var _iWrite = 0;
var _iRead = 0;
int get length => _buf.length;
Future<T> next() {
if (_iRead == _buf.length) {
_buf.add(Completer<T>());
}
final fut = _buf[_iRead].future;
_iRead += 1;
_cleanup();
return fut;
}
void add(T val) {
if (_iWrite == _buf.length) {
final prm = Completer<T>();
_buf.add(prm);
}
_buf[_iWrite].complete(val);
_iWrite += 1;
_cleanup();
}
void _cleanup() {
final minI = _iWrite < _iRead ? _iWrite : _iRead;
if (minI > 0) {
_buf.removeRange(0, minI);
_iWrite -= minI;
_iRead -= minI;
}
}
}
Dart Language:
This is plugin issue
xmpp_stone plugin
I can't fix this issue
anyone known please replay
[![enter image description here]
https://i.stack.imgur.com/FHe0O.png
Dart Language:
This is plugin issue
xmpp_stone plugin
I can't fix this issue
anyone known please replay
[![enter image description here]
https://i.stack.imgur.com/FHe0O.png
This is full code issue in xmpp_stone plugin
import 'dart:io';
import 'package:xmpp_stone/src/logger/Log.dart';
import 'package:console/console.dart';
import 'dart:async';
import 'dart:convert';
import 'package:xmpp_stone/xmpp_stone.dart' as xmpp;
import 'package:image/image.dart' as image;
final String TAG = 'example';
class ExampleConnectionStateChangedListener implements xmpp.ConnectionStateChangedListener {
late xmpp.Connection _connection;
late xmpp.MessagesListener _messagesListener;
StreamSubscription<String>? subscription;
ExampleConnectionStateChangedListener(xmpp.Connection connection, xmpp.MessagesListener messagesListener) {
_connection = connection;
_messagesListener = messagesListener;
_connection.connectionStateStream.listen(onConnectionStateChanged);
}
#override
void onConnectionStateChanged(xmpp.XmppConnectionState state) {
if (state == xmpp.XmppConnectionState.Ready) {
Log.d(TAG, 'Connected');
_connection.getMamModule().queryAll();
var vCardManager = xmpp.VCardManager(_connection);
vCardManager.getSelfVCard().then((vCard) {
if (vCard != null) {
Log.d(TAG, 'Your info' + vCard.buildXmlString());
}
});
var messageHandler = xmpp.MessageHandler.getInstance(_connection);
var rosterManager = xmpp.RosterManager.getInstance(_connection);
messageHandler.messagesStream.listen(_messagesListener.onNewMessage);
sleep(const Duration(seconds: 1));
var receiver = 'yyy#gmail.com';
var receiverJid = xmpp.Jid.fromFullJid(receiver);
rosterManager.addRosterItem(xmpp.Buddy(receiverJid)).then((result) {
if (result.description != null) {
print("TAG, 'add roster'" + result.description!);
}
});
sleep(const Duration(seconds: 1));
vCardManager.getVCardFor(receiverJid).then((vCard) {
if (vCard != null) {
print("TAG, 'Receiver info'" + vCard.buildXmlString());
if (vCard != null && vCard.image != null) {
var file = File('test456789.jpg')..writeAsBytesSync(image.encodeJpg(vCard.image!));
print("TAG, IMAGE SAVED TO: ${file.path}");
}
}
});
var presenceManager = xmpp.PresenceManager.getInstance(_connection);
presenceManager.presenceStream.listen(onPresence);
}
}
void onPresence(xmpp.PresenceData event) {
Log.d(TAG, 'presence Event from ' + event.jid!.fullJid! + ' PRESENCE: ' + event.showElement.toString());
}
}
Stream<String> getConsoleStream() {
return Console.adapter.byteStream().map((bytes) {
var str = ascii.decode(bytes);
str = str.substring(0, str.length - 1);
return str;
});
}
class ExampleMessagesListener implements xmpp.MessagesListener {
#override
void onNewMessage(xmpp.MessageStanza? message) {
if (message!.body != null) {
Log.d(TAG ,format(
'New Message from {color.blue}${message.fromJid!.userAtDomain}{color.end} message: {color.red}${message.body}{color.end}'));
}
}
#override
void onChatMessage(xmpp.MessageStanza? message) {
print(message);
if (message!.body != null) {
Log.d(TAG,format(
'New Message from {color.blue}${message.fromJid!.userAtDomain}{color.end} message: {color.red}${message.body}{color.end}'));
}
}
}
sendmessageforxmpp(){
var userAtDomain = 'xxx#gmail.com';
var password = 'password';
var jid = xmpp.Jid.fromFullJid(userAtDomain);
var account = xmpp.XmppAccountSettings(userAtDomain, jid.local,
jid.domain, password, 5222, resource: 'xmppstone');
var connection = xmpp.Connection(account);
var receiver = 'yyy#gmail.com';
var receiverJid = xmpp.Jid.fromFullJid(receiver);
Log.d(TAG, receiverJid.fullJid.toString());
var messageHandler =
xmpp.MessageHandler.getInstance(connection);
messageHandler.sendMessage(receiverJid, "str");
}
Your problem is that you are not using xmpp_stone correctly and therefore ends up in a situation where the internal state of xmpp_stone does not match what the developer of the package have intended.
I do, however, think the package are badly designed in such a way that wrong usage are very likely to happen so I would not blame you for getting into trouble.
The problem is the following in your code:
var connection = xmpp.Connection(account);
// ..
var messageHandler = xmpp.MessageHandler.getInstance(connection);
messageHandler.sendMessage(receiverJid, "str");
You are here creating a Connection but the underlying socket are never created. The default value for the internal state of Connection are XmppConnectionState.Idle. But when you are later trying to sendMessage, your code ends up running this from the package:
void write(message) {
Log.xmppp_sending(message);
if (isOpened()) {
_socket!.write(message);
}
}
bool isOpened() {
return state != XmppConnectionState.Closed &&
state != XmppConnectionState.ForcefullyClosed &&
state != XmppConnectionState.Closing &&
state != XmppConnectionState.SocketOpening;
}
The isOpened() ends up returning true since it sees XmppConnectionState.Idle as an open state where messages are allowed to be sent.
But that is not the case here since we never asked Connection to open actually do any connection and therefore _socket ends up being null. Since the package are trying to do ! on null, the application crashes.
For an actual solution, we can get inspired from the example implementation from xmpp_dart:
https://github.com/vukoye/xmpp_dart/blob/master/example/example.dart
We can here see they have a connection.connect(); call. But, I am going to guess this really only works because the example are not going to use the connection right after this call. The problem is that it is implemented like the following:
void connect() {
if (_state == XmppConnectionState.Closing) {
_state = XmppConnectionState.WouldLikeToOpen;
}
if (_state == XmppConnectionState.Closed) {
_state = XmppConnectionState.Idle;
}
if (_state == XmppConnectionState.Idle) {
openSocket();
}
}
Future<void> openSocket() async {
connectionNegotatiorManager.init();
setState(XmppConnectionState.SocketOpening);
try {
return await Socket.connect(account.host ?? account.domain, account.port)
.then((Socket socket) {
// if not closed in meantime
if (_state != XmppConnectionState.Closed) {
setState(XmppConnectionState.SocketOpened);
So connect() returns void but calls openSocket() which does return a Future that would be able to tell us when the connection are actually ready.
I would therefore instead suggest using openSocket() directly and make your sendmessageforxmpp() method async so we can await on the connection being open.
So your code should look like:
Future<void> sendmessageforxmpp() async {
var userAtDomain = 'xxx#gmail.com';
var password = 'password';
var jid = xmpp.Jid.fromFullJid(userAtDomain);
var account = xmpp.XmppAccountSettings(
userAtDomain, jid.local, jid.domain, password, 5222,
resource: 'xmppstone');
var connection = xmpp.Connection(account);
await connection.openSocket(); // <--- the important change :)
var receiver = 'yyy#gmail.com';
var receiverJid = xmpp.Jid.fromFullJid(receiver);
Log.d(TAG, receiverJid.fullJid.toString());
var messageHandler = xmpp.MessageHandler.getInstance(connection);
messageHandler.sendMessage(receiverJid, "str");
}
This error is usually occurring when you use the bang operator (!) on a nullable value that was not properly initialized, like
yourvariable!.somefield
The above assumes that yourvariable will not be null as this point. If it's null, then reality is in conflict with the assumption I have just described.
I want to mix MediaStreamTrack objects in Dart using the package:universal_html/js.dart library.
JsAudioContext audioContext = JsAudioContext();
audioContext.initialize();
var senders = await call!.peerConnection!.getSenders();
for (var sender in senders) {
for (var track in senderTracks) {
if (sender.track!.id != track.id) {
audioContext.connect(track);
}
}
}
But WebRtc hides the jsTrack native object inside the MediaStreamTrackWeb object.
How can I access this object ?
Is there anyone have an idea ?
I found the solution using the js_bindings library.
The library's MediaStream.getTracks() method throws a type error.
I solved this problem using js_util interop.
JsAudioContext.dart:
import 'dart:convert';
import 'package:flutter_webrtc/flutter_webrtc.dart' as webrtc;
import 'package:dart_webrtc/src/media_stream_track_impl.dart' as track_impl;
import 'package:js_bindings/js_bindings.dart' as js_bindings;
import 'package:universal_html/html.dart' as html;
import 'dart:js_util' as js_util;
class JsAudioContext {
js_bindings.AudioContext? audioContext;
js_bindings.MediaStreamAudioDestinationNode? destinationNode;
JsAudioContext() {
audioContext = js_bindings.AudioContext();
}
void createMediaStreamDestination() {
destinationNode = audioContext?.createMediaStreamDestination();
}
void connect(webrtc.MediaStreamTrack? trackWeb) {
track_impl.MediaStreamTrackWeb mediaStreamTrackWeb =
trackWeb as track_impl.MediaStreamTrackWeb;
html.MediaStreamTrack htmlTrack = mediaStreamTrackWeb.jsTrack;
var sourceStream = audioContext?.createMediaStreamSource(
js_bindings.MediaStream([htmlTrack as js_bindings.MediaStreamTrack]));
sourceStream?.connect(destinationNode!);
}
webrtc.MediaStreamTrack getMixedTrack() {
List<dynamic> outputTrack =
js_util.callMethod(destinationNode!.stream, 'getTracks', []);
webrtc.MediaStreamTrack rtcTrack = track_impl.MediaStreamTrackWeb(
outputTrack.toList()[0] as html.MediaStreamTrack);
return rtcTrack;
}
}
sip_call_event_service.dart:
#override
Future startConference(List<SipCallData> activeCallList) async {
List<webrtc.MediaStreamTrack> receivedTracks = <webrtc.MediaStreamTrack>[];
for (var item in activeCallList) {
Call? call = sipuaHelper!.findCall(item.id!);
var receives = await call!.peerConnection!.getReceivers();
for (var element in receives) {
receivedTracks.add(element.track!);
}
}
JsAudioContext jsAudioContext = JsAudioContext();
for (var item in activeCallList) {
Call? call = sipuaHelper!.findCall(item.id!);
jsAudioContext.createMediaStreamDestination();
var receivers = await call!.peerConnection!.getReceivers();
for (var receiver in receivers) {
for (var track in receivedTracks) {
if (receiver.track!.id != track.id) {
jsAudioContext.connect(track);
}
}
}
var senders = await call.peerConnection!.getSenders();
for (var sender in senders) {
jsAudioContext.connect(sender.track);
}
await senders.first.replaceTrack(jsAudioContext.getMixedTrack());
}
}
here is the code can anyone tell how can I setSkuDetails()
as I was using vision one now I update it to 4
However, setSku and setType seem to be deprecated in the BillingFlowParams.Builder class. Instead, we should be using setSkuDetails(SkuDetails).
private void BillingFunction() {
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
// Establish connection to billing client
mBillingClient = BillingClient.newBuilder(MainActivity.this).setListener(MainActivity.this).build();
mBillingClient.startConnection(new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(#NonNull BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The billing client is ready. You can query purchases here.
getPricesMonthlyTime();
getPricesYearlyTime();
getPricesONeTime();
}
}
#Override
public void onBillingServiceDisconnected() {
//TODO implement your own retry policy
Toast.makeText(MainActivity.this, getResources().getString(R.string.billing_connection_failure), Toast.LENGTH_SHORT);
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
continue_button.setOnClickListener(view -> {
if (select_radio_one.getVisibility() == View.VISIBLE) {
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSkuDetails()
.build();
BillingResult responseCode = mBillingClient.launchBillingFlow(MainActivity.this, flowParams);
brandDialogInAppPurchase.dismiss();
} else if (select_radio_two.getVisibility() == View.VISIBLE) {
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSkuDetails()
.build();
BillingResult responseCode = mBillingClient.launchBillingFlow(MainActivity.this, flowParams);
brandDialogInAppPurchase.dismiss();
} else if (select_radio_three.getVisibility() == View.VISIBLE) {
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSkuDetails()
.build();
BillingResult responseCode = mBillingClient.launchBillingFlow(MainActivity.this, flowParams);
brandDialogInAppPurchase.dismiss();
} else {
Toast.makeText(MainActivity.this, "Nothing selected", Toast.LENGTH_SHORT).show();
}
});
// queryPrefPurchases();
queryPurchases();
}
You should send the object skuDetail.
To do so you need to retrieve it by calling querySkuDetailsAsync().
fun querySkuDetails() {
val skuList = ArrayList<String>()
skuList.add("premium_upgrade")
skuList.add("gas")
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skuList).setType(SkuType.INAPP)
// leverage querySkuDetails Kotlin extension function
val skuDetailsResult = withContext(Dispatchers.IO) {
billingClient.querySkuDetails(params.build())
}
// Process the result.
}
EDIT: Problem wasn't related to Timer or HttpServer, it was dart.io sleep function pausing everything. It is clearly described in documentation, my bad.
//
I have weird problem with HttpClient working in server code. I call
client.getUrl(Uri.parse(url)).then((HttpClientRequest response) => response.close()).then(HttpBodyHandler.processResponse).then((HttpClientResponseBody body) {
print(body.response.statusCode);
from Timer object and it never reach print step.
It is almost copy and paste code from previous version, which wasn't called from Timer but from HttpRequest. Working code is in my question [here][1].
It fails on the long line, I suspect that it is a last Future it never reach (HttpClientResponseBody).
Timer object is created like this (just test code):
main() {
t = new Timer.periodic(new Duration(minutes: period), (Timer t) => hit());
}
void hit() {
if (new DateTime.now().hour == 17) {
print("syncing rock");
loadUrlBody(furl + filter).then((content) {
print("content loaded");
//edit:
okay, here is the source, it might be some trivial problem..which I can't figure out for two days :-D
import 'dart:async';
import 'dart:io';
import 'package:http_server/http_server.dart';
import 'package:slack/slack_io.dart' as slack;
Timer t;
bool check;
final period = 1;
final furl = "https://****.tpondemand.com";
final filter = "somefilter";
main() {
t = new Timer.periodic(new Duration(minutes: period), (Timer t) => hit());
}
void hit() {
if (new DateTime.now().hour == 17) {
print("syncing rock");
loadUrlBody(furl + filter).then((content) {
print("content loaded");
Map parsedMap = content.body;
handleMap(parsedMap);
});
sleep(new Duration(minutes: 60));
} else {
print("no time to rock " + new DateTime.now().toString());
sleep(new Duration(minutes: period * 10));
}
}
Future loadUrlBody(String url) {
final c = new Completer();
HttpClient client = new HttpClient();
client.addCredentials(Uri.parse("https://****.tpondemand.com/api"), "tprealm", new HttpClientBasicCredentials("user", "password"));
client.getUrl(Uri.parse(url)).then((HttpClientRequest response) => response.close()).then(HttpBodyHandler.processResponse).then((HttpClientResponseBody body) {
print(body.response.statusCode);
c.complete(body);
});
return c.future;
}
void send2Slack(String m) {
slack.Message message = new slack.Message()..text = m;
slack.token = 'token';
slack.team = 'team';
slack.send(message);
}
void handleMap(Map valueMap) {
final Duration lostInTime = new Duration(days: 30);
var sb = new StringBuffer();
sb.write('K o m p o s t \n');
for (var item in valueMap["Items"]) {
if (item['CreateDate'] == null) item['CreateDate'] = '/Date(1403167885000+0100)/';
if (item['ModifyDate'] == null) item['ModifyDate'] = '/Date(1403167885000+0100)/';
if (item['LastCommentDate'] == null) item['LastCommentDate'] = '/Date(1403167885000+0100)/';
DateTime moonLanding = new DateTime.fromMillisecondsSinceEpoch(int.parse(item['CreateDate'].substring(6, 19)));
DateTime modifyLanding = new DateTime.fromMillisecondsSinceEpoch(int.parse(item['ModifyDate'].substring(6, 19)));
DateTime commentLanding = new DateTime.fromMillisecondsSinceEpoch(int.parse(item['LastCommentDate'].substring(6, 19)));
DateTime lastChangeLanding = (modifyLanding.isBefore(commentLanding)) ? commentLanding : modifyLanding;
Duration difference = new DateTime.now().difference(lastChangeLanding);
if (moonLanding.add(lostInTime).isBefore(new DateTime.now()) && difference.inDays > 4) {
sb
..write('<https://****.tpondemand.com/entity/')
..write(item['Id'])
..write('|')
..write(item['Name'])
..write('> last change: ')
..write(difference.inDays)
..write(' days ago \n');
}
;
}
send2Slack(sb.toString());
print("sent to Slack");
sb.clear();
}
I created similar code but I can't reproduce your problem.
So basically this does work when called from a Timer.
import 'dart:io';
import 'dart:async';
import 'package:http_server/http_server.dart';
Timer t;
final period = 1;
void main(args) {
t = new Timer.periodic(new Duration(minutes: period), (Timer t) => hit());
}
void hit() {
loadUrlBody('http://www.google.com')
.then((HttpClientResponseBody b) => print('hit: ${b.response.statusCode}'));
}
Future loadUrlBody(String url) {
print('executing');
HttpClient client = new HttpClient();
// commented out because I have no server where I can use it
// HttpClient client = new HttpClient()
// ..addCredentials(Uri.parse("https://****.tpondemand.com/api"), "tprealm", new HttpClientBasicCredentials("user", "password"));
return client.getUrl(Uri.parse(url)) // <== return is important here
.then((HttpClientRequest response) => response.close())
.then(HttpBodyHandler.processResponse)
.then((HttpClientResponseBody body) {
print('body: (${new DateTime.now()}) ${body.response.statusCode}');
return body; // <== this value is the value the next 'then' receives.
// for example x in: loadUrlBody('http://someurl').then(x) => doSomething(x));
});
}
You don't need to use a Completer. Completer are for more complicated used cases where for example one method returns a Completer and for example an eventHandler completes it.
You just have to ensure that you return a Future everywhere. then always returns a Future. The value of the returned Future is the value returned inside then.