| // 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 'package:flutter/shell.dart' show shell; |
| import 'package:logging/logging.dart'; |
| import 'package:v23discovery/discovery.dart' as discovery; |
| |
| export 'package:v23discovery/discovery.dart' show UpdateTypes; |
| |
| import '../models/all.dart' as model; |
| |
| final Logger log = new Logger('discovery/client'); |
| |
| const String _discoveryMojoUrl = |
| 'https://discovery.syncslides.mojo.v.io/discovery.mojo'; |
| |
| // TODO(aghassemi): We should make this the same between Flutter and Java apps when |
| // they can actually talk to each other. |
| const String _presentationInterfaceName = |
| 'v.io/release/projects/syncslides/dart/presentation'; |
| |
| final discovery.Client _discoveryClient = |
| new discovery.Client(shell.connectToService, _discoveryMojoUrl); |
| |
| final Map<String, discovery.Advertiser> _advertisers = new Map(); |
| discovery.Scanner _scanner; |
| |
| Future advertise(model.PresentationAdvertisement presentation) async { |
| if (_advertisers.containsKey(presentation.key)) { |
| return _advertisers[presentation.key]; |
| } |
| |
| log.info('Started advertising ${presentation.deck.name}.'); |
| |
| Map<String, String> serviceAttrs = new Map(); |
| serviceAttrs['deckid'] = presentation.deck.key; |
| serviceAttrs['name'] = presentation.deck.name; |
| serviceAttrs['thumbnailkey'] = presentation.deck.thumbnail.key; |
| serviceAttrs['presentationid'] = presentation.key; |
| discovery.Advertisement service = new discovery.Advertisement('s/v', []); |
| ..attributes = serviceAttrs; |
| |
| _advertisers[presentation.key] = await _discoveryClient.advertise(service); |
| |
| log.info('Advertised ${presentation.deck.name} under ${presentation.key}.'); |
| } |
| |
| Future stopAdvertising(String presentationId) async { |
| if (!_advertisers.containsKey(presentationId)) { |
| // Not advertised, nothing to stop. |
| return; |
| } |
| await _advertisers[presentationId].stop(); |
| _advertisers.remove(presentationId); |
| } |
| |
| // Transforms a stream of discovery services to PresentationAdvertisement model objects. |
| StreamTransformer toPresentationUpdate = new StreamTransformer.fromHandlers( |
| handleData: (discovery.Update u, EventSink<PresentationUpdate> sink) { |
| String key = u.attributes['presentationid']; |
| log.info('Found presentation ${u.attributes['name']} under $key.'); |
| // Ignore our own advertised services. |
| if (_advertisers.containsKey(key)) { |
| log.info( |
| 'Presentation ${u.attributes['name']} was advertised by us; ignoring.'); |
| return; |
| } |
| |
| model.Deck deck = new model.Deck(u.attributes['deckid'], u.attributes['name'], |
| new model.BlobRef(u.attributes['thumbnailkey'])); |
| var syncgroupName = u.addresses[0]; |
| var thumbnailSyncgroupName = u.addresses[1]; |
| model.PresentationAdvertisement presentation = |
| new model.PresentationAdvertisement( |
| key, deck, syncgroupName, thumbnailSyncgroupName); |
| |
| sink.add(new PresentationUpdate._internal(presentation, u.updateType)); |
| }); |
| |
| Future<PresentationScanner> scan() async { |
| var query = 'v.InterfaceName = "$_presentationInterfaceName"'; |
| _scanner = await _discoveryClient.scan(query); |
| |
| log.info('Scan started.'); |
| |
| return new PresentationScanner._internal( |
| _scanner.onUpdate.transform(toPresentationUpdate)); |
| } |
| |
| Future stopScan() async { |
| if (_scanner == null) { |
| // No scan call has been made before. |
| return; |
| } |
| await _scanner.stop(); |
| _scanner = null; |
| } |
| |
| class PresentationUpdate { |
| model.PresentationAdvertisement presentation; |
| discovery.UpdateTypes updateType; |
| PresentationUpdate._internal(this.presentation, this.updateType); |
| } |
| |
| class PresentationScanner { |
| Stream<PresentationUpdate> onUpdate; |
| PresentationScanner._internal(this.onUpdate); |
| } |