SyncSlides: Make targets for deploying the binaries
and creating a shortcut so app can be installed on devices.
Also couple of small fixes:
-Database structure is now created in advance.
-Bug fix where I was aborting a batch before reading
all the data.
Closes https://github.com/vanadium/syncslides/issues/15
Closes https://github.com/vanadium/syncslides/issues/3
Change-Id: I5ef2d897cddcfe69bec83f6876cf5427168d6ba6
diff --git a/.gitignore b/.gitignore
index faa2bc5..c9f8a00 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,5 @@
/dart/snapshot_blob.bin
/dart/app.flx
/dart/build
+/dart/shortcut_commands
.atom
diff --git a/dart/MOJO_VERSION b/dart/MOJO_VERSION
index a8fb06d..3d22a47 100644
--- a/dart/MOJO_VERSION
+++ b/dart/MOJO_VERSION
@@ -1 +1 @@
-08b1d8fc1e0296569b628cae2e7611988497b273
\ No newline at end of file
+4503cee3f5f2d3bc3ad46636af0a1029fb22e108
diff --git a/dart/Makefile b/dart/Makefile
index ec6b260..670dcc3 100644
--- a/dart/Makefile
+++ b/dart/Makefile
@@ -7,7 +7,6 @@
DEVICE_ID := $(shell adb devices | sed -n $(DEVICE_NUM_PLUS_ONE)p | awk '{ print $$1; }')
DEVICE_FLAG := --target-device $(DEVICE_ID)
MOUNTTABLE_ADDR := /192.168.86.254:8101
-NAME_FLAG := --name=$(DEVICE_ID)
# Currently the only way to pass arguments to the app is by using a file.
SETTINGS_FILE := /sdcard/syncslides_settings.json
@@ -21,6 +20,8 @@
VLOG_FLAGS = --v=$(VLOG) --logtostderr=true
endif
+SYNCBASE_ARGS := https://syncbase.syncslides.mojo.v.io/syncbase_server.mojo --root-dir=$(SYNCBASE_DATA_DIR) --v23.namespace.root=$(MOUNTTABLE_ADDR) --name=$(DEVICE_ID) $(VLOG_FLAGS)
+
default: run
.PHONY: dartanalyzer
@@ -38,21 +39,59 @@
upgrade-packages:
pub upgrade
+.PHONY: build
+build: packages
+ pub run flutter_tools build
+
+GS_BUCKET_PATH := gs://mojo_services/syncslides
+GS_BUCKET_URL := storage.googleapis.com/mojo_services/syncslides
+SYNCSLIDES_URL = mojo://$(GS_BUCKET_URL)/app.flx
+
+APP_FLX_FILE := $(PWD)/build/app.flx
+SYNCBASE_MOJO_DIR := $(PWD)/packages/syncbase/mojo_services/android
+DISCOVERY_MOJO_DIR := $(PWD)/packages/v23discovery/mojo_services/android
+MOJO_SHELL_CMD_PATH := /data/local/tmp/org.chromium.mojo.shell.cmd
+
+SYNCSLIDES_SHORTCUT_NAME := SyncSlides
+SYNCSLIDES_ICON := https://avatars0.githubusercontent.com/u/9374332?v=3&s=200
+
+define GENERATE_SHORTCUT_FILE
+ sed -e "s;%GS_BUCKET_URL%;$1;g" -e "s;%SYNCBASE_FLAGS%;$2;g" \
+ shortcut_template > shortcut_commands
+endef
+
+.PHONY: install
+install: build deploy
+ $(call GENERATE_SHORTCUT_FILE,$(GS_BUCKET_URL),$(SYNCBASE_ARGS))
+ adb -s $(DEVICE_ID) push -p shortcut_commands $(MOJO_SHELL_CMD_PATH)
+ adb -s $(DEVICE_ID) shell chmod 555 $(MOJO_SHELL_CMD_PATH)
+ adb -s $(DEVICE_ID) shell 'echo $(SETTINGS_JSON) > $(SETTINGS_FILE)'
+ $(MOJO_DIR)/src/mojo/devtools/common/mojo_run --android $(DEVICE_FLAG) "mojo:shortcut $(SYNCSLIDES_SHORTCUT_NAME) $(SYNCSLIDES_URL) $(SYNCSLIDES_ICON)"
+
+.PHONY: uninstall
+uninstall:
+ adb -s $(DEVICE_ID) uninstall org.chromium.mojo.shell
+ adb -s $(DEVICE_ID) shell rm -f $(MOJO_SHELL_CMD_PATH)
+# TODO(aghassemi): Is there a way to remove the shortcut via adb?
+
+.PHONY: deploy
+deploy: packages
+ gsutil cp $(APP_FLX_FILE) $(GS_BUCKET_PATH)
+ gsutil cp $(SYNCBASE_MOJO_DIR)/syncbase_server.mojo $(GS_BUCKET_PATH)
+ gsutil cp $(DISCOVERY_MOJO_DIR)/discovery.mojo $(GS_BUCKET_PATH)
+ gsutil -m acl set -R -a public-read $(GS_BUCKET_PATH)
+
# Usage example:
# DEVICE_NUM=1 make run
# DEVICE_NUM=2 make run
-run: packages
+run: build
adb -s $(DEVICE_ID) shell 'echo $(SETTINGS_JSON) > $(SETTINGS_FILE)'
- pub run flutter_tools build && pub run flutter_tools run_mojo \
+ 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" \
- --args-for="https://syncslides.mojo.v.io/packages/syncbase/mojo_services/android/syncbase_server.mojo \
- --root-dir=$(SYNCBASE_DATA_DIR) \
- --v23.namespace.root=$(MOUNTTABLE_ADDR) \
- $(NAME_FLAG) \
- $(VLOG_FLAGS)" \
+ --map-origin="https://syncbase.syncslides.mojo.v.io/=$(SYNCBASE_MOJO_DIR)" \
+ --map-origin="https://discovery.syncslides.mojo.v.io/=$(DISCOVERY_MOJO_DIR)" \
+ --args-for="$(SYNCBASE_ARGS)" \
$(DEVICE_FLAG) \
$(REUSE_FLAG) \
--no-config-file
@@ -67,15 +106,11 @@
run4:
DEVICE_NUM=4 make run
-.PHONY: uninstall
-uninstall:
- adb -s $(DEVICE_ID) uninstall org.chromium.mojo.shell
-
.PHONY: clean
clean:
rm -f app.flx snapshot_blob.bin
rm -rf packages
- adb -s $(DEVICE_ID) shell run-as org.chromium.mojo.shell rm $(SETTINGS_FILE)
+ adb -s $(DEVICE_ID) shell run-as org.chromium.mojo.shell rm $(SETTINGS_FILE) settings_commands
.PHONY: clean-syncbase
clean-syncbase:
diff --git a/dart/lib/components/askquestion.dart b/dart/lib/components/askquestion.dart
index e69ac67..0dad214 100644
--- a/dart/lib/components/askquestion.dart
+++ b/dart/lib/components/askquestion.dart
@@ -25,6 +25,8 @@
return new Text('Not in a presentation.');
}
+ // TODO(aghassemi): Switch to multi-line input when support is added.
+ // https://github.com/flutter/flutter/issues/627
var input = new Input(placeholder: 'Your question',
onSubmitted: (String questionText) async {
await appActions.askQuestion(
diff --git a/dart/lib/discovery/client.dart b/dart/lib/discovery/client.dart
index 5fac314..87b2d47 100644
--- a/dart/lib/discovery/client.dart
+++ b/dart/lib/discovery/client.dart
@@ -13,7 +13,7 @@
final Logger log = new Logger('discovery/client');
const String v23DiscoveryMojoUrl =
- 'https://syncslides.mojo.v.io/packages/v23discovery/mojo_services/android/discovery.mojo';
+ '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.
diff --git a/dart/lib/stores/syncbase/actions.dart b/dart/lib/stores/syncbase/actions.dart
index c1cec97..86b1b7d 100644
--- a/dart/lib/stores/syncbase/actions.dart
+++ b/dart/lib/stores/syncbase/actions.dart
@@ -14,18 +14,18 @@
Future addDeck(model.Deck deck) async {
log.info("Adding deck ${deck.name}...");
- sb.SyncbaseTable tb = await _getDecksTable();
+ sb.SyncbaseTable tb = _getDecksTable();
await tb.put(deck.key, UTF8.encode(deck.toJson()));
log.info("Deck ${deck.name} added.");
}
Future removeDeck(String deckKey) async {
- sb.SyncbaseTable tb = await _getDecksTable();
+ sb.SyncbaseTable tb = _getDecksTable();
tb.deleteRange(new sb.RowRange.prefix(deckKey));
}
Future setSlides(String deckKey, List<model.Slide> slides) async {
- sb.SyncbaseTable tb = await _getDecksTable();
+ sb.SyncbaseTable tb = _getDecksTable();
slides.forEach((slide) async {
// TODO(aghassemi): Use batching.
@@ -51,7 +51,7 @@
// Is the current user driving the presentation?
if (deckState.presentation.isDriving(_state.user)) {
// Update the common slide number for the presentation.
- sb.SyncbaseTable tb = await _getPresentationsTable();
+ sb.SyncbaseTable tb = _getPresentationsTable();
await tb.put(
keyutil.getPresentationCurrSlideNumKey(
deckId, deckState.presentation.key),
@@ -123,7 +123,7 @@
setDefaultsAndJoin() async {
// Set the current slide number to 0.
- sb.SyncbaseTable tb = await _getPresentationsTable();
+ sb.SyncbaseTable tb = _getPresentationsTable();
await tb.put(
keyutil.getPresentationCurrSlideNumKey(deckId, presentation.key),
[0]);
@@ -229,7 +229,7 @@
'Cannot ask a question because deck is not part of a presentation');
}
- sb.SyncbaseTable tb = await _getPresentationsTable();
+ sb.SyncbaseTable tb = _getPresentationsTable();
String questionId = uuidutil.createUuid();
model.Question question = new model.Question(
@@ -255,12 +255,12 @@
// Blobs
Future putBlob(String key, List<int> bytes) async {
- sb.SyncbaseTable tb = await _getBlobsTable();
+ sb.SyncbaseTable tb = _getBlobsTable();
await tb.put(key, bytes);
}
Future<List<int>> getBlob(String key) async {
- sb.SyncbaseTable tb = await _getBlobsTable();
+ sb.SyncbaseTable tb = _getBlobsTable();
return tb.get(key);
}
}
@@ -270,7 +270,7 @@
Future _setPresentationDriver(
String deckId, String presentationId, model.User driver) async {
- sb.SyncbaseTable tb = await _getPresentationsTable();
+ sb.SyncbaseTable tb = _getPresentationsTable();
await tb.put(keyutil.getPresentationDriverKey(deckId, presentationId),
UTF8.encode(driver.toJson()));
}
@@ -279,27 +279,14 @@
return '${settings.mounttable}/${settings.deviceId}/%%sync/$uuid';
}
-Future<sb.SyncbaseTable> _getTable(String tableName) async {
- sb.SyncbaseDatabase sbDb = await sb.getDatabase();
- sb.SyncbaseTable tb = sbDb.table(tableName);
- try {
- await tb.create(sb.createOpenPerms());
- } catch (e) {
- if (!errorsutil.isExistsError(e)) {
- throw e;
- }
- }
- return tb;
+sb.SyncbaseTable _getDecksTable() {
+ return sb.database.table(decksTableName);
}
-Future<sb.SyncbaseTable> _getDecksTable() {
- return _getTable(decksTableName);
+sb.SyncbaseTable _getPresentationsTable() {
+ return sb.database.table(presentationsTableName);
}
-Future<sb.SyncbaseTable> _getPresentationsTable() {
- return _getTable(presentationsTableName);
-}
-
-Future<sb.SyncbaseTable> _getBlobsTable() {
- return _getTable(blobsTableName);
+sb.SyncbaseTable _getBlobsTable() {
+ return sb.database.table(blobsTableName);
}
diff --git a/dart/lib/stores/syncbase/store.dart b/dart/lib/stores/syncbase/store.dart
index 3c8913f..64e705f 100644
--- a/dart/lib/stores/syncbase/store.dart
+++ b/dart/lib/stores/syncbase/store.dart
@@ -48,8 +48,9 @@
_asyncInits();
}
- // Initializations that we must wait for before considering store initialized.
+ // Initializations that we must wait for before considering store initalized.
Future _syncInits() async {
+ await _createSyncbaseHierarchy();
_state._user = await identity.getUser();
_state._settings = await settings.getSettings();
}
@@ -59,11 +60,8 @@
Future _asyncInits() async {
// TODO(aghassemi): Use the multi-table scan and watch API when ready.
// See https://github.com/vanadium/issues/issues/923
- sb.SyncbaseDatabase db = await sb.getDatabase();
- // Make sure all tables exist.
- await _ensureTablesExist();
for (String table in [decksTableName, presentationsTableName]) {
- _getInitialValuesAndStartWatching(db, table);
+ _getInitialValuesAndStartWatching(table);
}
_startScanningForPresentations();
}
@@ -103,20 +101,19 @@
discovery.startScan();
}
- Future _getInitialValuesAndStartWatching(
- sb.SyncbaseDatabase sbDb, String table) async {
+ Future _getInitialValuesAndStartWatching(String table) async {
// TODO(aghassemi): Ideally we wouldn't need an initial query and can configure
// watch to give both initial values and future changes.
// See https://github.com/vanadium/issues/issues/917
- var batchDb =
- await sbDb.beginBatch(sb.SyncbaseClient.batchOptions(readOnly: true));
+ var batchDb = await sb.database
+ .beginBatch(sb.SyncbaseClient.batchOptions(readOnly: true));
var resumeMarker = await batchDb.getResumeMarker();
// Get initial values in a batch.
String query = 'SELECT k, v FROM $table';
Stream<sb.Result> results = batchDb.exec(query);
// NOTE(aghassemi): First row is always the name of the columns, so we skip(1).
- results.skip(1).forEach((sb.Result result) => _onChange(
+ await results.skip(1).forEach((sb.Result result) => _onChange(
table,
sb.WatchChangeTypes.put,
UTF8.decode(result.values[0]),
@@ -125,7 +122,7 @@
await batchDb.abort();
// Start watching from batch's resume marker.
- var stream = sbDb.watch(table, '', resumeMarker);
+ var stream = sb.database.watch(table, '', resumeMarker);
stream.listen((sb.WatchChange change) =>
_onChange(table, change.changeType, change.rowKey, change.valueBytes));
}
@@ -245,9 +242,22 @@
});
}
- Future _ensureTablesExist() async {
- await _getDecksTable();
- await _getPresentationsTable();
- await _getBlobsTable();
+ Future<sb.SyncbaseTable> _createTable(String tableName) async {
+ sb.SyncbaseTable tb = sb.database.table(tableName);
+ try {
+ await tb.create(sb.createOpenPerms());
+ } catch (e) {
+ if (!errorsutil.isExistsError(e)) {
+ throw e;
+ }
+ }
+ return tb;
+ }
+
+ Future _createSyncbaseHierarchy() async {
+ await sb.init();
+ await _createTable(decksTableName);
+ await _createTable(presentationsTableName);
+ await _createTable(blobsTableName);
}
}
diff --git a/dart/lib/syncbase/client.dart b/dart/lib/syncbase/client.dart
index dc95299..c4d0c73 100644
--- a/dart/lib/syncbase/client.dart
+++ b/dart/lib/syncbase/client.dart
@@ -15,41 +15,40 @@
final Logger log = new Logger('syncbase/client');
const String syncbaseMojoUrl =
- 'https://syncslides.mojo.v.io/packages/syncbase/mojo_services/android/syncbase_server.mojo';
+ 'https://syncbase.syncslides.mojo.v.io/syncbase_server.mojo';
const appName = 'syncslides';
const dbName = 'syncslides';
-SyncbaseDatabase _db;
-// Returns the database handle for the SyncSlides app.
-Future<SyncbaseDatabase> getDatabase() async {
- if (_db != null) {
- return _db;
- }
+SyncbaseDatabase database;
- // Initialize Syncbase app and database.
+// Initializes Syncbase by creating the app and the database.
+Future init() async {
SyncbaseClient sbClient =
new SyncbaseClient(shell.connectToService, syncbaseMojoUrl);
SyncbaseApp sbApp = await _createApp(sbClient);
- _db = await _createDb(sbApp);
-
- return _db;
+ database = await _createDb(sbApp);
}
Future createSyncgroup(
String mounttable, String syncgroupName, prefixes) async {
- SyncbaseDatabase sbDb = await getDatabase();
- SyncbaseSyncgroup sg = sbDb.syncgroup(syncgroupName);
+ SyncbaseSyncgroup sg = database.syncgroup(syncgroupName);
var sgSpec = SyncbaseClient.syncgroupSpec(prefixes,
perms: createOpenPerms(), mountTables: [mounttable]);
var myInfo = SyncbaseClient.syncgroupMemberInfo(syncPriority: 1);
- await sg.create(sgSpec, myInfo);
+ try {
+ await sg.create(sgSpec, myInfo);
+ } catch (e) {
+ if (!errorsutil.isExistsError(e)) {
+ throw e;
+ }
+ }
+
log.info('Created syncgroup $syncgroupName');
}
Future joinSyncgroup(String syncgroupName) async {
- SyncbaseDatabase sbDb = await getDatabase();
- SyncbaseSyncgroup sg = sbDb.syncgroup(syncgroupName);
+ SyncbaseSyncgroup sg = database.syncgroup(syncgroupName);
var myInfo = SyncbaseClient.syncgroupMemberInfo(syncPriority: 1);
await sg.join(myInfo);
diff --git a/dart/pubspec.lock b/dart/pubspec.lock
index f808f5e..979700a 100644
--- a/dart/pubspec.lock
+++ b/dart/pubspec.lock
@@ -214,7 +214,7 @@
syncbase:
description: syncbase
source: hosted
- version: "0.0.15"
+ version: "0.0.18"
test:
description: test
source: hosted
diff --git a/dart/pubspec.yaml b/dart/pubspec.yaml
index 9a6892e..efbe82b 100644
--- a/dart/pubspec.yaml
+++ b/dart/pubspec.yaml
@@ -5,7 +5,7 @@
path: "../../../../../flutter/packages/flutter"
logging: ">=0.11.2 <0.12.0"
mojo_services: ">=0.4.5 <0.5.0"
- syncbase: ">=0.0.15 <0.1.0"
+ syncbase: ">=0.0.18 <0.1.0"
v23discovery: ">=0.0.4 < 0.1.0"
uuid: ">=0.5.0 <0.6.0"
dev_dependencies:
diff --git a/dart/shortcut_template b/dart/shortcut_template
new file mode 100644
index 0000000..5330e32
--- /dev/null
+++ b/dart/shortcut_template
@@ -0,0 +1,6 @@
+--map-origin=http://flutter/=https://storage.googleapis.com/mojo/flutter/e80be08b5794930731171c151a73140b8f75b0f7/android-arm/
+--url-mappings=mojo:flutter=http://flutter/flutter.mojo
+--enable-multiprocess
+--map-origin=https://syncbase.syncslides.mojo.v.io=https://%GS_BUCKET_URL%/
+--map-origin=https://discovery.syncslides.mojo.v.io=https://%GS_BUCKET_URL%/
+--args-for=%SYNCBASE_FLAGS%