syncslides: Updating to the latest versions of Flutter,
Mojo and v23Discovery and fixing breaking changes.

-Adding some logging.
-Using asset bundl so images load when phone is
not connected Fixes: https://github.com/vanadium/syncslides/issues/1

Please note that Flutter no longer publishes on Pub and
therefore we all need to have a clone of Flutter at a
specific place (until we add a Flutter profile)

I have added two files that specify the commit # for
flutter and mojo that I have tested the app in. This is
temporary and will go away when we have Mojo and Flutter
profiles.

Change-Id: If658a9cebb3fb649686dcfb5b88dfc164b74c6c9
diff --git a/dart/FLUTTER_VERSION b/dart/FLUTTER_VERSION
new file mode 100644
index 0000000..fb36760
--- /dev/null
+++ b/dart/FLUTTER_VERSION
@@ -0,0 +1 @@
+a35b214d891efaeddfb4b4270ebe222dfab16a8a
diff --git a/dart/MOJO_VERSION b/dart/MOJO_VERSION
new file mode 100644
index 0000000..df87133
--- /dev/null
+++ b/dart/MOJO_VERSION
@@ -0,0 +1 @@
+7a690c5c0344946ed74402d61d473502bc03ad77
diff --git a/dart/Makefile b/dart/Makefile
index 922d478..be5398d 100644
--- a/dart/Makefile
+++ b/dart/Makefile
@@ -37,8 +37,8 @@
 # DEVICE_NUM=1 make run
 # DEVICE_NUM=2 make run
 run: packages
-	pub run sky_tools build && pub run sky_tools run_mojo \
-	--mojo-path $(MOJO_DIR)/src/mojo/devtools/common/mojo_run \
+	pub run flutter_tools build && pub run flutter_tools run_mojo \
+	--mojo-path $(MOJO_DIR)/src \
 	--android --mojo-debug -- --enable-multiprocess \
 	--map-origin="https://syncslides.mojo.v.io/=$(PWD)" \
 	--map-origin="https://discovery.mojo.v.io/=$(JIRI_ROOT)/release/mojo/discovery/gen/mojo/android" \
diff --git a/dart/README.md b/dart/README.md
index ced3a3b..fc704b7 100644
--- a/dart/README.md
+++ b/dart/README.md
@@ -4,10 +4,17 @@
 
 # Prerequisites
 
-##Mojo
+## Mojo
 
 Currently, development is heavily tied to an existing installation of [Mojo](https://github.com/domokit/mojo). Please ensure that your Mojo checkout is located at `$MOJO_DIR` and has been build for Android. Instructions are available [here](https://github.com/domokit/mojo#mojo).
 
+We only guarantee compatibility with the version of Mojo as specified in MOJO_VERSION.
+
+## Flutter
+
+A clone of https://github.com/flutter/flutter/ at the commit # specified in FLUTTER_VERSION file must be available in a directory
+called `flutter` at the same level as $V23_ROOT directory.
+
 ## Dart
 
 Flutter depends on a relatively new version of the Dart SDK. Therefore, please ensure that you have installed the following version or greater:
diff --git a/dart/flutter.yaml b/dart/flutter.yaml
index 2da469b..00911c6 100644
--- a/dart/flutter.yaml
+++ b/dart/flutter.yaml
@@ -3,3 +3,14 @@
   - name: navigation/arrow_back
   - name: navigation/arrow_forward
   - name: content/add
+assets:
+  - assets/images/defaults/thumbnail.png
+  - assets/images/sample_decks/vanadium/1.jpg
+  - assets/images/sample_decks/vanadium/2.jpg
+  - assets/images/sample_decks/vanadium/3.jpg
+  - assets/images/sample_decks/vanadium/4.jpg
+  - assets/images/sample_decks/vanadium/5.jpg
+  - assets/images/sample_decks/vanadium/6.jpg
+  - assets/images/sample_decks/baku/thumb.png
+  - assets/images/sample_decks/vanadium/thumb.png
+  - assets/images/sample_decks/pitch/thumb.png
diff --git a/dart/lib/components/deckgrid.dart b/dart/lib/components/deckgrid.dart
index 479e995..f092be9 100644
--- a/dart/lib/components/deckgrid.dart
+++ b/dart/lib/components/deckgrid.dart
@@ -86,7 +86,7 @@
   subtitleWidget = _stopWrapping(subtitleWidget);
   var footer = _buildBoxFooter(deckData.name, subtitleWidget);
   var box = _buildCard(deckData.key, [thumbnail, footer], () {
-    Navigator.of(context).push(new PageRoute(
+    Navigator.of(context).push(new MaterialPageRoute(
         builder: (context) => new SlideListPage(deckData.key, deckData.name)));
   });
 
diff --git a/dart/lib/components/slidelist.dart b/dart/lib/components/slidelist.dart
index cbbb17f..e27dbc7 100644
--- a/dart/lib/components/slidelist.dart
+++ b/dart/lib/components/slidelist.dart
@@ -9,8 +9,6 @@
 import '../stores/store.dart';
 import '../styles/common.dart' as style;
 
-import '../utils/keyvalue.dart';
-
 import 'slideshow.dart';
 
 // SlideListPage is the full page view of the list of slides for a deck.
@@ -36,9 +34,8 @@
         child: new Icon(icon: 'navigation/arrow_forward'), onPressed: () async {
       model.PresentationAdvertisement presentation =
           await _store.startPresentation(deckId);
-      Navigator
-          .of(context)
-          .push(new PageRoute(builder: (context) => new SlideshowPage(deckId)));
+      Navigator.of(context).push(new MaterialPageRoute(
+          builder: (context) => new SlideshowPage(deckId)));
     });
   }
 }
@@ -69,18 +66,13 @@
   }
 
   Widget build(BuildContext context) {
-    // Create a list of <SlideNumber, Slide> pairs.
-    List<KeyValue<int, model.Slide>> slidesWithPosition = [];
-    for (var i = 0; i < _slides.length; i++) {
-      slidesWithPosition.add(new KeyValue(i, _slides[i]));
-    }
     return new ScrollableList(
         itemExtent: style.Size.listHeight,
-        items: slidesWithPosition,
-        itemBuilder: (context, kv) =>
-            _buildSlide(context, kv.key.toString(), kv.value, onTap: () {
-              _store.setCurrSlideNum(config.deckId, kv.key);
-              Navigator.of(context).push(new PageRoute(
+        items: _slides,
+        itemBuilder: (context, value, index) =>
+            _buildSlide(context, index.toString(), value, onTap: () {
+              _store.setCurrSlideNum(config.deckId, index);
+              Navigator.of(context).push(new MaterialPageRoute(
                   builder: (context) => new SlideshowPage(config.deckId)));
             }));
   }
diff --git a/dart/lib/discovery/client.dart b/dart/lib/discovery/client.dart
index e2a766f..d120433 100644
--- a/dart/lib/discovery/client.dart
+++ b/dart/lib/discovery/client.dart
@@ -6,11 +6,14 @@
 import 'dart:convert';
 
 import 'package:flutter/services.dart' show shell;
+import 'package:logging/logging.dart';
 import 'package:v23discovery/discovery.dart' as v23discovery;
 
 import '../models/all.dart' as model;
 import '../utils/asset.dart' as assetutil;
 
+final Logger log = new Logger('discovery/client');
+
 const String v23DiscoveryMojoUrl =
     'https://syncslides.mojo.v.io/packages/v23discovery/mojo_services/android/discovery.mojo';
 
@@ -38,6 +41,7 @@
     new Map();
 
 Future advertise(model.PresentationAdvertisement presentation) async {
+  log.info('Started advertising ${presentation.deck.name}.');
   if (_advertiseCalls.containsKey(presentation.key)) {
     // We are already advertising for this presentation.
     return _advertiseCalls[presentation.key].responseFuture;
@@ -49,7 +53,7 @@
   v23discovery.Service serviceInfo = new v23discovery.Service()
     ..instanceUuid = UTF8.encode(presentation.key)
     ..interfaceName = presentationInterfaceName
-    ..instanceName = ''
+    ..instanceName = presentation.key
     ..attrs = serviceAttrs
     ..addrs = [presentation.syncgroupName];
 
@@ -62,6 +66,7 @@
       new ProxyResponseFuturePair(advertiser, advertiseResponseFuture);
 
   await advertiseResponseFuture;
+  log.info('Advertised ${presentation.deck.name} under ${presentation.key}.');
 }
 
 // Tracks advertisements that are in the middle of being stopped.
@@ -93,7 +98,7 @@
 
   stoppingCall.then((_) {
     _advertiseCalls.remove(presentationId);
-    _stoppingAdvertisingCalls.remove(presentationId);
+  log.info('Stopped advertising ${presentationId}.');
   }).catchError((e) {
     _stoppingAdvertisingCalls.remove(presentationId);
     throw e;
@@ -117,6 +122,7 @@
   _scanCall = new ProxyResponseFuturePair(scanner, scannerResponseFuture);
 
   await scannerResponseFuture;
+  log.info('Scan started.');
 }
 
 // Tracks whether we are already in the middle of stopping scan.
@@ -144,7 +150,7 @@
 
   _stoppingScanCall.then((_) {
     _scanCall = null;
-    _stoppingScanCall = null;
+  log.info('Scan stopped.');
   }).catchError((e) {
     _stoppingScanCall = null;
     throw e;
@@ -154,14 +160,18 @@
 class ScanHandler extends v23discovery.ScanHandler {
   found(v23discovery.Service s) async {
     String key = UTF8.decode(s.instanceUuid);
+    log.info('Found presentation ${s.attrs['name']} under $key.');
     // Ignore our own advertised services.
     if (_advertiseCalls.containsKey(key)) {
+      log.info(
+          'Presentation ${s.attrs['name']} was advertised by this device itself, ignoring it.');
       return;
     }
 
     // TODO(aghassemi): For now we use the default thumbnail. We need to find a way
     // to fetch the actual thumbnail from the other side.
-    var thumbnail = await assetutil.getRawBytes(assetutil.defaultThumbnailUrl);
+    var thumbnail =
+        await assetutil.getRawBytes(assetutil.defaultThumbnailAssetKey);
     model.Deck deck =
         new model.Deck(s.attrs['deckid'], s.attrs['name'], thumbnail.toList());
     var syncgroupName = s.addrs[0];
@@ -174,6 +184,7 @@
   lost(List<int> instanceId) {
     String presentationId = UTF8.decode(instanceId);
     // Ignore our own advertised services.
+    log.info('Lost presentation $presentationId.');
     _onLostEmitter.add(presentationId);
   }
 }
diff --git a/dart/lib/main.dart b/dart/lib/main.dart
index 0503e05..85e2522 100644
--- a/dart/lib/main.dart
+++ b/dart/lib/main.dart
@@ -3,13 +3,23 @@
 // license that can be found in the LICENSE file.
 
 import 'package:flutter/material.dart';
+import 'package:logging/logging.dart';
 
 import 'styles/common.dart' as style;
 import 'components/deckgrid.dart';
 
 void main() {
+  _initLogging();
+
   runApp(new MaterialApp(
       theme: style.theme,
       title: 'SyncSlides',
       routes: {'/': (RouteArguments args) => new DeckGridPage()}));
 }
+
+void _initLogging() {
+  Logger.root.level = Level.ALL;
+  Logger.root.onRecord.listen((LogRecord rec) {
+    print('\nSyncSlides: ${rec.time}: ${rec.message}');
+  });
+}
diff --git a/dart/lib/stores/syncbase_store.dart b/dart/lib/stores/syncbase_store.dart
index 2a98a33..e6d6ddf 100644
--- a/dart/lib/stores/syncbase_store.dart
+++ b/dart/lib/stores/syncbase_store.dart
@@ -165,10 +165,12 @@
 
   Future<int> getCurrSlideNum(String deckId) async {
     sb.SyncbaseTable tb = await _getDecksTable();
-    var v = await tb.get(keyutil.getCurrSlideNumKey(deckId));
-    if (v == null || v.isEmpty) {
+    String key = keyutil.getCurrSlideNumKey(deckId);
+    // TODO(aghassemi): Run exist and get in a batch.
+    if (await tb.row(key).exists()) {
       return 0;
     }
+    var v = await tb.get(key);
     return v[0];
   }
 
diff --git a/dart/lib/utils/asset.dart b/dart/lib/utils/asset.dart
index bb001a3..fbeaa73 100644
--- a/dart/lib/utils/asset.dart
+++ b/dart/lib/utils/asset.dart
@@ -5,17 +5,15 @@
 import 'dart:async';
 import 'dart:typed_data';
 
+import 'package:mojo/core.dart';
+
 import 'package:flutter/services.dart' as services;
 
-Map<String, Uint8List> _assetCache = new Map<String, Uint8List>();
-Future<Uint8List> getRawBytes(String url) async {
-  if (_assetCache.containsKey(url)) {
-    return _assetCache[url];
-  }
-  services.Response response = await services.fetchBody(url);
-  var bytes = new Uint8List.fromList(response.body.buffer.asUint8List());
-  _assetCache[url] = bytes;
+Future<Uint8List> getRawBytes(String assetKey) async {
+  MojoDataPipeConsumer pipe = await services.rootBundle.load(assetKey);
+  ByteData data = await DataPipeDrainer.drainHandle(pipe);
+  Uint8List bytes = new Uint8List.fromList(data.buffer.asUint8List());
   return bytes;
 }
 
-String defaultThumbnailUrl = 'assets/images/defaults/thumbnail.png';
+String defaultThumbnailAssetKey = 'assets/images/defaults/thumbnail.png';
diff --git a/dart/lib/utils/keyvalue.dart b/dart/lib/utils/keyvalue.dart
deleted file mode 100644
index 8fa83d7..0000000
--- a/dart/lib/utils/keyvalue.dart
+++ /dev/null
@@ -1,11 +0,0 @@
-// 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.
-
-// KeyValue presents a generic pair of key and value objects.
-class KeyValue<T1, T2> {
-  T1 key;
-  T2 value;
-
-  KeyValue(this.key, this.value);
-}
diff --git a/dart/pubspec.lock b/dart/pubspec.lock
index a8af949..bf3850a 100644
--- a/dart/pubspec.lock
+++ b/dart/pubspec.lock
@@ -30,8 +30,10 @@
     source: hosted
     version: "0.0.7"
   cassowary:
-    description: cassowary
-    source: hosted
+    description:
+      path: "../../../../../flutter/packages/cassowary"
+      relative: true
+    source: path
     version: "0.1.7"
   charcode:
     description: charcode
@@ -45,10 +47,6 @@
     description: collection
     source: hosted
     version: "1.1.3"
-  concepts:
-    description: concepts
-    source: hosted
-    version: "0.2.0"
   convert:
     description: convert
     source: hosted
@@ -61,21 +59,27 @@
     description: csslib
     source: hosted
     version: "0.12.2"
-  either:
-    description: either
-    source: hosted
-    version: "0.1.8"
   fixnum:
     description: fixnum
     source: hosted
     version: "0.9.1+2"
   flutter:
-    description: flutter
-    source: hosted
-    version: "0.0.18"
+    description:
+      path: "../../../../../flutter/packages/flutter"
+      relative: true
+    source: path
+    version: "0.0.20"
+  flutter_tools:
+    description:
+      path: "../../../../../flutter/packages/flutter_tools"
+      relative: true
+    source: path
+    version: "0.0.38"
   flx:
-    description: flx
-    source: hosted
+    description:
+      path: "../../../../../flutter/packages/flx"
+      relative: true
+    source: path
     version: "0.0.10"
   glob:
     description: glob
@@ -116,35 +120,33 @@
   mojo:
     description: mojo
     source: hosted
-    version: "0.4.2"
+    version: "0.4.3"
   mojo_apptest:
     description: mojo_apptest
     source: hosted
-    version: "0.2.7"
+    version: "0.2.8"
   mojo_sdk:
     description: mojo_sdk
     source: hosted
-    version: "0.2.1"
+    version: "0.2.2"
   mojo_services:
     description: mojo_services
     source: hosted
-    version: "0.4.4"
+    version: "0.4.5"
   mojom:
     description: mojom
     source: hosted
-    version: "0.2.7"
+    version: "0.2.8"
   mustache4dart:
     description: mustache4dart
     source: hosted
     version: "1.0.10"
   newton:
-    description: newton
-    source: hosted
+    description:
+      path: "../../../../../flutter/packages/newton"
+      relative: true
+    source: path
     version: "0.1.5"
-  option:
-    description: option
-    source: hosted
-    version: "1.1.0"
   package_config:
     description: package_config
     source: hosted
@@ -169,22 +171,10 @@
     description: pub_semver
     source: hosted
     version: "1.2.3"
-  quiver:
-    description: quiver
-    source: hosted
-    version: "0.21.4"
   shelf:
     description: shelf
     source: hosted
     version: "0.6.4+1"
-  shelf_path:
-    description: shelf_path
-    source: hosted
-    version: "0.1.7"
-  shelf_route:
-    description: shelf_route
-    source: hosted
-    version: "0.13.5"
   shelf_static:
     description: shelf_static
     source: hosted
@@ -196,15 +186,11 @@
   sky_engine:
     description: sky_engine
     source: hosted
-    version: "0.0.49"
+    version: "0.0.56"
   sky_services:
     description: sky_services
     source: hosted
-    version: "0.0.50"
-  sky_tools:
-    description: sky_tools
-    source: hosted
-    version: "0.0.37"
+    version: "0.0.56"
   source_map_stack_trace:
     description: source_map_stack_trace
     source: hosted
@@ -237,10 +223,6 @@
     description: typed_data
     source: hosted
     version: "1.1.1"
-  uri:
-    description: uri
-    source: hosted
-    version: "0.11.0"
   utf:
     description: utf
     source: hosted
@@ -252,7 +234,7 @@
   v23discovery:
     description: v23discovery
     source: hosted
-    version: "0.0.2"
+    version: "0.0.4"
   vector_math:
     description: vector_math
     source: hosted
diff --git a/dart/pubspec.yaml b/dart/pubspec.yaml
index b44d99c..45a372b 100644
--- a/dart/pubspec.yaml
+++ b/dart/pubspec.yaml
@@ -1,9 +1,12 @@
 name: syncslides
 description: A simple multi-device presentation system built on Flutter and Syncbase.
 dependencies:
-  flutter: ">=0.0.16 <0.1.0"
+  flutter:
+    path: "../../../../../flutter/packages/flutter"
+  logging: ">=0.11.2 <0.12.0"
   syncbase: ">=0.0.9 <0.1.0"
-  v23discovery: ">=0.0.1 < 0.1.0"
+  v23discovery: ">=0.0.4 < 0.1.0"
   uuid: ">=0.5.0 <0.6.0"
 dev_dependencies:
-  sky_tools: ">=0.0.27 <0.1.0"
+  flutter_tools:
+    path: "../../../../../flutter/packages/flutter_tools"