blob: 7cb605e01e62b44c7cedcdfe3b23412518c51902 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:v23discovery/discovery.dart' as discovery;
import 'package:flutter/services.dart' show shell;
import 'package:uuid/uuid.dart';
const String _discoveryMojoUrl =
'https://discovery.mojo.v.io/discovery.mojo';
void main() {
runApp(
new MaterialApp(
title: "Discovery Demo",
routes: <String, RouteBuilder>{
'/': (RouteArguments args) => new DiscoveryDemo()
}
)
);
}
final discovery.Client _discoveryClient =
new discovery.Client(shell.connectToService, _discoveryMojoUrl);
class DiscoveryDemo extends StatefulComponent {
@override
State createState() => new DiscoveryDemoState();
}
final String instanceName = 'sample${new Uuid().v4()}';
const String interfaceName = 'examples.discovery.sample';
// The color of the icon to signify that the action is running.
final highlightColor = Colors.blue[500];
class DiscoveryDemoState extends State {
// The message to advertise.
// TODO: After v23proxy works for dart send this via rpc rather than
// attributes.
String message = 'Hello world!';
bool isAdvertising = false;
bool isScanning = false;
// The currently running advertisement. This is used to stop the
// advertisement when we are done.
discovery.Advertiser adv = null;
// The currently running scanner. This is used to stop the
// scanning when we are done.
discovery.Scanner scanner = null;
// A SplayTreeMap is used so the iteration order is stable.
Map<String, String> foundMessages = new SplayTreeMap();
Future _startAdvertising() async {
Map<String, String> attrs = new Map();
attrs['message'] = message;
var service = new discovery.Service()
..attrs = attrs
..interfaceName = interfaceName
..instanceName = instanceName
// TODO put in a real endpoint here. An empty array results in an
// advertisement error.
..addrs = ['localhost:4000'];
adv = await _discoveryClient.advertise(service);
setState(() { isAdvertising = true;});
}
Future _stopAdvertising() async {
await adv?.stop();
adv = null;
setState(() { isAdvertising = false; });
}
void _toggleAdvertising() {
if (!isAdvertising) {
_startAdvertising();
} else {
_stopAdvertising();
}
}
Future _maybeUpdateAdv() async {
if (!isAdvertising) {
return;
}
// Don't call _stopAdvertising so the button's background color doesn't
// change briefly while we update the advertisement.
await adv.stop();
await _startAdvertising();
}
Future _startScanning() async {
var query = 'v.InterfaceName = "${interfaceName}"';
scanner = await _discoveryClient.scan(query);
scanner.onUpdate.listen((update) {
// Ignore advertisements for this device.
if (update.service.instanceName == instanceName) {
return;
}
setState(() {
var instanceName = update.service.instanceName;
if (update.updateType == discovery.UpdateType.found) {
foundMessages[instanceName] = update.service.attrs['message'];
} else {
foundMessages.remove(instanceName);
}
});
});
setState(() { isScanning = true;});
}
Future _stopScanning() async {
await scanner.stop();
scanner = null;
setState(() { isScanning = false; });
}
void _toggleScanning() {
if (!isScanning) {
_startScanning();
} else {
_stopScanning();
}
}
// Builds the row that contains the message input and the buttons to start
// and stop discovery.
Widget _buildButtonBar() {
var advColor;
if (isAdvertising) {
advColor = highlightColor;
}
var advButton = new IconButton(
icon: 'notification/tap_and_play',
color: advColor,
onPressed: _toggleAdvertising
);
var scanColor;
if (isScanning) {
scanColor = highlightColor;
}
var scanButton = new IconButton(
icon: 'action/search',
color: scanColor,
onPressed: _toggleScanning
);
var input = new Input(
initialValue: message,
onChanged: (String value) {
message = value;
_maybeUpdateAdv();
});
return new Row(
children: [
new Text('Message'),
new Flexible(child: input), advButton, scanButton]);
}
Widget build(BuildContext context) {
List<Widget> children = [_buildButtonBar()];
foundMessages.forEach((k, v) {
children.add(new Text('${k}: ${foundMessages[k]}', key:new Key(k)));
});
return new Scaffold(
toolBar: new ToolBar(
center: new Text("Discovery Demo")
),
body: new Material(
child: new Column(children: children)
)
);
}
}