website: automated tests for the Your First App tutorial
-Refactoring the project stub to work for both quickstart
and Your First App
-Simplifying how CAT and EOF lines are hidden
-Making changes and adding tests for the Your First App
so the code compiles ( but does not work )
Change-Id: I1254f50c0c6452becfdb94768256f64ef48f85cb
diff --git a/Makefile b/Makefile
index a8ce0d1..1cfaceb 100644
--- a/Makefile
+++ b/Makefile
@@ -104,6 +104,7 @@
tutJavaFortune = tutorials/java/fortune
syncbaseAndroidQuickstart = syncbase/quickstart
+syncbaseAndroidFirstApp = syncbase/first-app
# Scripts that 'complete' the named tutorials, creating all relevant files
# (code, credentials, etc.) but skipping ephemeral steps like starting servers,
@@ -382,7 +383,8 @@
content/$(tutJavaAndroid).md
depsOneBigSyncbaseAndroidTest = \
- content/$(syncbaseAndroidQuickstart).md
+ content/$(syncbaseAndroidQuickstart).md \
+ content/$(syncbaseAndroidFirstApp).md
.PHONY: test
test: test-site test-tutorials-core test-tutorials-java test-syncbase-android
diff --git a/content/syncbase/first-app.md b/content/syncbase/first-app.md
index 0d99fb3..183e6b1 100644
--- a/content/syncbase/first-app.md
+++ b/content/syncbase/first-app.md
@@ -11,15 +11,21 @@
our [mailing list](/community/mailing-lists.html) for updates.
{{/ helpers.warning }}
+{{# helpers.hidden }}
+<!-- @setupEnvironment @test -->
+```
+export PROJECT_DIR=$(mktemp -d "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX")
+cp -r $JIRI_ROOT/website/tools/android_project_stubs/example/* $PROJECT_DIR
+```
+{{/ helpers.hidden }}
+
# Introduction
In this quick tutorial, we will build a *Dice Roller* Android app where
one can simply generate a random number between 1-6 and have it sync
across multiple devices peer-to-peer, even with Wi-Fi turned off!
-<div class="rows">
- <img style="width:250px" src="/images/syncbase-dice-device-1.gif">
-</div>
+<img style="width:250px" src="/images/syncbase-dice-device-1.gif">
# Setup
This tutorial uses Android Studio, but feel free to use your IDE of choice.
@@ -33,10 +39,13 @@
## Install Syncbase
Add the following to your `build.gradle` file.
+<!-- @addSyncbaseDependency @test -->
```
+cat - <<EOF >> $PROJECT_DIR/app/build.gradle
dependencies {
- compile 'io.v:vanadium-android:2.1.3+'
+ compile 'io.v:syncbase:0.1.4'
}
+EOF
```
## Setup Cloud Syncbase
@@ -55,9 +64,11 @@
## Initialize Syncbase
**MainActivity.java**
+<!-- @generateMainActivity @test -->
```
+cat - <<EOF | sed 's/{{.*}}//' > $PROJECT_DIR/app/src/main/java/io/v/syncbase/example/MainActivity.java
{{# helpers.codedim }}
-package io.v.myfirstsyncbaseapp;
+package io.v.syncbase.example;
import android.support.v7.app.AppCompatActivity;
@@ -74,23 +85,31 @@
super.onCreate(savedInstanceState);
{{/ helpers.codedim }}
- User currUser = Users.loginWithDefaultAccount();
- DatabaseOptions dbOpt = new DatabaseOptions();
- dbOpt.cloudSyncbaseAddress = "<Your Cloud Syncbase Address>"
- dbOpt.cloudSyncbaseBlessing = "<Your Cloud Syncbase Blessing>"
+ Syncbase.DatabaseOptions options = new Syncbase.DatabaseOptions();
+ // dbOpt.cloudSyncbaseAddress = "<Your Cloud Syncbase Address>";
+ // dbOpt.cloudSyncbaseBlessing = "<Your Cloud Syncbase Blessing>";
- Database db = Syncbase.getDatabase();
+ Syncbase.database(new Syncbase.DatabaseCallback() {
+ @Override
+ public void onSuccess(final Database db) {
- Log.i("info", "Welcome: " + currUser.getEmail());
+ // Use database to interact with Syncbase.
+
+ Log.i("info", "Syncbase is ready");
+ }
+ }, options);
+
{{# helpers.codedim }}
setContentView(R.layout.activity_main);
}
}
{{/ helpers.codedim }}
+EOF
```
+
Now, let's run the app to make sure login and Syncbase initialization are working.
-After running, you should see `Welcome <email>` in logcat under Android Monitor
+After running, you should see `Syncbase is ready` in logcat under Android Monitor
or in the console.
# UI Code
@@ -99,9 +118,11 @@
Here is the UI code
**activity_main.xml**
+
+<!-- @generateMainActivityXML @test -->
```
-{{# helpers.codedim }}
-<?xml version="1.0" encoding="utf-8"?>
+cat - <<EOF | sed 's/{{.*}}//' > $PROJECT_DIR/app/src/main/res/layout/activity_main.xml
+{{# helpers.codedim }}<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@@ -110,7 +131,7 @@
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
- tools:context="io.v.myfirstsyncbaseapp.MainActivity">
+ tools:context="io.v.syncbase.example.MainActivity">
{{/ helpers.codedim }}
<TextView
{{# helpers.codedim }}
@@ -139,11 +160,20 @@
android:layout_centerHorizontal="true" />
</RelativeLayout>
{{/ helpers.codedim }}
+EOF
```
+
Running the project at this point should result in the following UI:
<img style="width:250px" src="/images/syncbase-dice-1.png" alt="Screenshot of the Dice Roll app">
+{{# helpers.hidden }}
+<!-- @firstStepCompile_mayTakeMinutes @test -->
+```
+cd $PROJECT_DIR && ./gradlew assembleRelease
+```
+{{/ helpers.hidden }}
+
# Data Binding
The data model for this app is simple. We just need a single collection (`dice`)
and a single key/value pair (`'result'`, `int`) to store the result of the dice
@@ -162,9 +192,11 @@
Now let's hook up this model to our code.
+<!-- @updateMainActivity @test -->
```
+cat - <<EOF | sed 's/{{.*}}//' > $PROJECT_DIR/app/src/main/java/io/v/syncbase/example/MainActivity.java
{{# helpers.codedim }}
-package io.v.myfirstsyncbaseapp;
+package io.v.syncbase.example;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
@@ -182,70 +214,77 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
+
super.onCreate(savedInstanceState);
- User currUser = Users.loginWithDefaultAccount();
+ Syncbase.DatabaseOptions options = new Syncbase.DatabaseOptions();
+ // dbOpt.cloudSyncbaseAddress = "<Your Cloud Syncbase Address>";
+ // dbOpt.cloudSyncbaseBlessing = "<Your Cloud Syncbase Blessing>";
- DatabaseOptions dbOpt = new DatabaseOptions();
- dbOpt.cloudSyncbaseAddress = "<Your Cloud Syncbase Address>"
- dbOpt.cloudSyncbaseBlessing = "<Your Cloud Syncbase Blessing>"
+ Syncbase.database(new Syncbase.DatabaseCallback() {
+ @Override
+ public void onSuccess(final Database db) {
- Database db = Syncbase.getDatabase();
+ // Use database to interact with Syncbase.
- Log.i("info", "Welcome: " + currUser.getEmail());
+ Log.i("info", "Syncbase is ready");
+ {{/ helpers.codedim }}
+
+ // On dice roll, put a new random number under key "result"
+ // in the "dice" collection.
+ final Button button = (Button) findViewById(R.id.buttonRoll);
+ button.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ int randomNumber = new Random().nextInt(6) + 1;
+
+ Collection diceCollection = db.collection("dice");
+ diceCollection.put("result", randomNumber);
+ }
+ });
+
+ // Watch the database and update the UI whenever a new value
+ // is encountered.
+ db.addWatchChangeHandler(new Database.WatchChangeHandler() {
+ @Override
+ public void onInitialState(Iterator<WatchChange> values) {
+ // onInitialState is called with any existing data in Syncbase.
+ // Since we only have a single collection, single key/value,
+ // there can only be 0 or 1 values.
+ if (values.hasNext()) {
+ int result = (int) values.next().getValue();
+ updateResult(result);
+ }
+ }
+
+ @Override
+ public void onChangeBatch(Iterator<WatchChange> changes) {
+ // onChangeBatch is called with any updates to the data.
+ // Since we only have a single collection, single key/value.
+ // there can only be 1 WatchChange whenever the value is mutated
+ // and the type of change would always be `put` in our case.
+ int result = (int) changes.next().getValue();
+ updateResult(result);
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ // Something went wrong. Watch is no longer active.
+ }
+ }, new Database.AddWatchChangeHandlerOptions());
+ {{# helpers.codedim }}
+ }
+ }, new Syncbase.DatabaseOptions());
setContentView(R.layout.activity_main);
-
- {{/ helpers.codedim }}
-
- // On dice roll, put a new random number under key "result"
- // in the "dice" collection.
- final Button button = (Button) findViewById(R.id.buttonRoll);
- button.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- int randomNumber = new Random().nextInt(6) + 1;
-
- Collection diceCollection = db.collection("dice");
- diceCollection.put("result", randomNumber);
- }
- });
-
- // Watch the database and update the UI whenever a new value
- // is encountered.
- db.removeWatchChangeHandler(new Database.WatchChangeHandler() {
-
- void onInitialState(Iterator<WatchChange> values) {
- // onInitialState is called with any existing data in Syncbase.
- // Since we only have a single collection, single key/value,
- // there can only be 0 or 1 values.
- if (values.hasNext()) {
- int result = (int) values.next().getValue(int.class);
- updateResult(result);
- }
- }
-
- void onChangeBatch(Iterator<WatchChange> changes) {
- // onChangeBatch is called with any updates to the data.
- // Since we only have a single collection, single key/value.
- // there can only be 1 WatchChange whenever the value is mutated
- // and the type of change would always be `put` in our case.
- int result = (int) changes.next().getValue(int.class);
- updateResult(result);
- }
-
- void onError(Exception e) {
- // Something went wrong. Watch is no longer active.
- }
- });
}
+{{/ helpers.codedim }}
private void updateResult(int newValue) {
final TextView result = (TextView) findViewById(R.id.textViewResult);
result.setText(String.valueOf(newValue));
}
-{{# helpers.codedim }}
}
-{{/ helpers.codedim }}
+EOF
```
# Running The App
@@ -271,9 +310,14 @@
ensure Bluetooth is enabled on both devices and turn off Wi-Fi, the dice rolls
should still sync between the devices just fine!
-<div class="rows">
- <img style="width:250px" src="/images/syncbase-dice-device-1.gif">
-</div>
+<img style="width:250px" src="/images/syncbase-dice-device-1.gif">
+
+{{# helpers.hidden }}
+<!-- @secondStepCompile_mayTakeMinutes @test -->
+```
+cd $PROJECT_DIR && ./gradlew assembleRelease
+```
+{{/ helpers.hidden }}
# Want to dive deeper?
Checkout the [Tutorial] to build a full-fledged Todo app and learn more Syncbase
diff --git a/content/syncbase/quickstart.md b/content/syncbase/quickstart.md
index 3024537..5932a3b 100644
--- a/content/syncbase/quickstart.md
+++ b/content/syncbase/quickstart.md
@@ -15,7 +15,7 @@
<!-- @setupEnvironment @test -->
```
export PROJECT_DIR=$(mktemp -d "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX")
-cp -r $JIRI_ROOT/website/tools/android_project_stubs/quickstart/* $PROJECT_DIR
+cp -r $JIRI_ROOT/website/tools/android_project_stubs/example/* $PROJECT_DIR
```
{{/ helpers.hidden }}
@@ -32,7 +32,6 @@
Syncbase's Android library is published to both [JCenter] and [MavenCentral].
To install the library, add the following to your `build.gradle` file.
-{{# helpers.hide_cat_eof_lines }}
<!-- @addSyncbaseDependency @test -->
```
cat - <<EOF >> $PROJECT_DIR/app/build.gradle
@@ -41,7 +40,6 @@
}
EOF
```
-{{/ helpers.hide_cat_eof_lines }}
# Setup Cloud Syncbase
Head to [https://sb-allocator.v.io/](https://sb-allocator.v.io/) to setup a free
@@ -59,13 +57,11 @@
# Use Syncbase
In your `MainActivity`, import Syncbase and read/write some data!
-
-{{# helpers.hide_cat_eof_lines }}
<!-- @generateMainActivity @test -->
```
-cat - <<EOF | sed 's/{{.*}}//' > $PROJECT_DIR/app/src/main/java/syncbase/io/v/quickstart/MainActivity.java
+cat - <<EOF | sed 's/{{.*}}//' > $PROJECT_DIR/app/src/main/java/io/v/syncbase/example/MainActivity.java
{{# helpers.codedim}}
-package syncbase.io.v.quickstart;
+package io.v.syncbase.example;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
@@ -85,7 +81,7 @@
Syncbase.database(new Syncbase.DatabaseCallback() {
@Override
- public void onSuccess(Database db) {
+ public void onSuccess(final Database db) {
// Use database to interact with Syncbase.
@@ -104,7 +100,6 @@
{{/ helpers.codedim}}
EOF
```
-{{/ helpers.hide_cat_eof_lines }}
**That's all!** You are now using Syncbase!
diff --git a/helpers.js b/helpers.js
index 5b2e8cc..ac7df4f 100644
--- a/helpers.js
+++ b/helpers.js
@@ -36,11 +36,6 @@
return '{#dim}{#dim-children}' + text + '{/dim-children}{/dim}\n';
};
-// Hides the "cat <<EOF > ..." and "EOF" lines in tutorial code.
-exports.hide_cat_eof_lines = function(text) {
- return '<div class="hide-cat-eof-lines">' + marked(text) + '</div>\n';
-};
-
////////////////////////////////////////
// Internal helpers
diff --git a/templates/layouts/syncbase.mustache b/templates/layouts/syncbase.mustache
index 22b5b2b..93547aa 100644
--- a/templates/layouts/syncbase.mustache
+++ b/templates/layouts/syncbase.mustache
@@ -4,7 +4,7 @@
<body>
{{> partials/syncbase_header }}
{{> partials/syncbase_sidebar }}
- <main>
+ <main class="hide-cat-eof-lines">
<h1 class="title">
{{# page.fullTitle }}{{ page.fullTitle }}{{/ page.fullTitle }}
{{^ page.fullTitle }}{{ page.title }}{{/ page.fullTitle }}
diff --git a/templates/layouts/syncbase_tutorial.mustache b/templates/layouts/syncbase_tutorial.mustache
index 9d6798b..ea0a9d9 100644
--- a/templates/layouts/syncbase_tutorial.mustache
+++ b/templates/layouts/syncbase_tutorial.mustache
@@ -4,7 +4,7 @@
<body>
{{> partials/syncbase_header }}
{{> partials/syncbase_sidebar }}
- <main>
+ <main class="hide-cat-eof-lines">
<h1 class="title">
{{# page.fullTitle }}{{ page.fullTitle }}{{/ page.fullTitle }}
{{^ page.fullTitle }}{{ page.title }}{{/ page.fullTitle }}
diff --git a/tools/android_project_stubs/quickstart/app/build.gradle b/tools/android_project_stubs/example/app/build.gradle
similarity index 92%
rename from tools/android_project_stubs/quickstart/app/build.gradle
rename to tools/android_project_stubs/example/app/build.gradle
index 0c8bf2e..d3da2a1 100644
--- a/tools/android_project_stubs/quickstart/app/build.gradle
+++ b/tools/android_project_stubs/example/app/build.gradle
@@ -5,7 +5,7 @@
buildToolsVersion "23.0.1"
defaultConfig {
- applicationId "syncbase.io.v.quickstart"
+ applicationId "io.v.syncbase.example"
minSdkVersion 21
targetSdkVersion 23
versionCode 1
diff --git a/tools/android_project_stubs/quickstart/app/src/main/AndroidManifest.xml b/tools/android_project_stubs/example/app/src/main/AndroidManifest.xml
similarity index 91%
rename from tools/android_project_stubs/quickstart/app/src/main/AndroidManifest.xml
rename to tools/android_project_stubs/example/app/src/main/AndroidManifest.xml
index a045031..26e5fb8 100644
--- a/tools/android_project_stubs/quickstart/app/src/main/AndroidManifest.xml
+++ b/tools/android_project_stubs/example/app/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="syncbase.io.v.quickstart" >
+ package="io.v.syncbase.example" >
<application
android:label="@string/app_name">
diff --git a/tools/android_project_stubs/quickstart/app/src/main/java/syncbase/io/v/quickstart/empty b/tools/android_project_stubs/example/app/src/main/java/io/v/syncbase/example/empty
similarity index 100%
rename from tools/android_project_stubs/quickstart/app/src/main/java/syncbase/io/v/quickstart/empty
rename to tools/android_project_stubs/example/app/src/main/java/io/v/syncbase/example/empty
diff --git a/tools/android_project_stubs/quickstart/app/src/main/res/layout/activity_main.xml b/tools/android_project_stubs/example/app/src/main/res/layout/activity_main.xml
similarity index 91%
rename from tools/android_project_stubs/quickstart/app/src/main/res/layout/activity_main.xml
rename to tools/android_project_stubs/example/app/src/main/res/layout/activity_main.xml
index ff2678f..de38c62 100644
--- a/tools/android_project_stubs/quickstart/app/src/main/res/layout/activity_main.xml
+++ b/tools/android_project_stubs/example/app/src/main/res/layout/activity_main.xml
@@ -8,7 +8,7 @@
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
- tools:context="syncbase.io.v.quickstart.MainActivity">
+ tools:context="io.v.syncbase.example.MainActivity">
<TextView
android:text="Hello Syncbase!"
diff --git a/tools/android_project_stubs/quickstart/app/src/main/res/values/colors.xml b/tools/android_project_stubs/example/app/src/main/res/values/colors.xml
similarity index 100%
rename from tools/android_project_stubs/quickstart/app/src/main/res/values/colors.xml
rename to tools/android_project_stubs/example/app/src/main/res/values/colors.xml
diff --git a/tools/android_project_stubs/quickstart/app/src/main/res/values/dimens.xml b/tools/android_project_stubs/example/app/src/main/res/values/dimens.xml
similarity index 100%
rename from tools/android_project_stubs/quickstart/app/src/main/res/values/dimens.xml
rename to tools/android_project_stubs/example/app/src/main/res/values/dimens.xml
diff --git a/tools/android_project_stubs/example/app/src/main/res/values/strings.xml b/tools/android_project_stubs/example/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..e6cfe01
--- /dev/null
+++ b/tools/android_project_stubs/example/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">Example Syncbase App</string>
+</resources>
diff --git a/tools/android_project_stubs/quickstart/app/src/main/res/values/styles.xml b/tools/android_project_stubs/example/app/src/main/res/values/styles.xml
similarity index 100%
rename from tools/android_project_stubs/quickstart/app/src/main/res/values/styles.xml
rename to tools/android_project_stubs/example/app/src/main/res/values/styles.xml
diff --git a/tools/android_project_stubs/quickstart/build.gradle b/tools/android_project_stubs/example/build.gradle
similarity index 100%
rename from tools/android_project_stubs/quickstart/build.gradle
rename to tools/android_project_stubs/example/build.gradle
diff --git a/tools/android_project_stubs/quickstart/gradle.properties b/tools/android_project_stubs/example/gradle.properties
similarity index 100%
rename from tools/android_project_stubs/quickstart/gradle.properties
rename to tools/android_project_stubs/example/gradle.properties
diff --git a/tools/android_project_stubs/quickstart/gradle/wrapper/gradle-wrapper.jar b/tools/android_project_stubs/example/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from tools/android_project_stubs/quickstart/gradle/wrapper/gradle-wrapper.jar
rename to tools/android_project_stubs/example/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tools/android_project_stubs/quickstart/gradle/wrapper/gradle-wrapper.properties b/tools/android_project_stubs/example/gradle/wrapper/gradle-wrapper.properties
similarity index 100%
rename from tools/android_project_stubs/quickstart/gradle/wrapper/gradle-wrapper.properties
rename to tools/android_project_stubs/example/gradle/wrapper/gradle-wrapper.properties
diff --git a/tools/android_project_stubs/quickstart/gradlew b/tools/android_project_stubs/example/gradlew
similarity index 100%
rename from tools/android_project_stubs/quickstart/gradlew
rename to tools/android_project_stubs/example/gradlew
diff --git a/tools/android_project_stubs/quickstart/gradlew.bat b/tools/android_project_stubs/example/gradlew.bat
similarity index 100%
rename from tools/android_project_stubs/quickstart/gradlew.bat
rename to tools/android_project_stubs/example/gradlew.bat
diff --git a/tools/android_project_stubs/quickstart/settings.gradle b/tools/android_project_stubs/example/settings.gradle
similarity index 100%
rename from tools/android_project_stubs/quickstart/settings.gradle
rename to tools/android_project_stubs/example/settings.gradle
diff --git a/tools/android_project_stubs/quickstart/QuickStart.iml b/tools/android_project_stubs/quickstart/QuickStart.iml
deleted file mode 100644
index f35b089..0000000
--- a/tools/android_project_stubs/quickstart/QuickStart.iml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module external.linked.project.id="QuickStart" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
- <component name="FacetManager">
- <facet type="java-gradle" name="Java-Gradle">
- <configuration>
- <option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
- <option name="BUILDABLE" value="false" />
- </configuration>
- </facet>
- </component>
- <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
- <exclude-output />
- <content url="file://$MODULE_DIR$">
- <excludeFolder url="file://$MODULE_DIR$/.gradle" />
- </content>
- <orderEntry type="inheritedJdk" />
- <orderEntry type="sourceFolder" forTests="false" />
- </component>
-</module>
\ No newline at end of file
diff --git a/tools/android_project_stubs/quickstart/app/src/main/res/values/strings.xml b/tools/android_project_stubs/quickstart/app/src/main/res/values/strings.xml
deleted file mode 100644
index f79b19f..0000000
--- a/tools/android_project_stubs/quickstart/app/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<resources>
- <string name="app_name">QuickStart</string>
-</resources>