When it comes to productivity, real-time collaboration is a staple of modern communication. An obvious example of this is, say, Google Docs. In this tutorial, my app Brain Drain takes that idea a step further, creating a freeform virtual whiteboard allowing users to collaborate as if they are all in the same conference room together. The app uses PubNub’s Data Stream Network (DSN), Firebase, and Google as an identity provider.
For this blog we will be using the following tools and third-party libraries:
- PubNub android SDK 4.0.7
- Firebase Remote Config, Auth and Database 9.4
- Google Play Services Auth 9.4
- Android Studio 2.1.2
- Google ChromeCast (Optional)
This series is split into three separate parts. In this first part, we lay the groundwork for the application by setting up the environment, implementing authentication, and defining our over-the-wire protocol (a.k.a dataStream).
Collaborative whiteboards require the use of both real-time signaling and persistence. A portion of this is satisfied by the various services we utilize. Firebase handles long term storage, authentication, and configuration while PubNub handles the real-time aggregation of drawing events. Google Chromecast allows the board collaborators to cast their board to a screen.
Part one focuses on configuration and the infrastructure required to build out the board. We will define the dataStream required to capture real-time collaboration and injecting the application with enough info to identify a collaborator, and capture collaboration events.
Getting a project like this set up can take quite a bit of time. We need to pull in all the proper dependencies, and ensure they all play well together. With that said, let’s get the project set up.
Setting Up the Environment
There are several steps that need to be taken to get your environment up and running. The first is to create the proper PubNub keyset and Firebase apps. Let’s start with Pubnub.
Head over to the PubNub Customer Portal and create a new application, called braindrain. On the default key, enable Stream Controller and Storage and Playback. The default settings for these add-ons is sufficient for now.
Firebase Auth Configuration
We are going to make use of several Firebase features in our Brain Drain solution. One of those features is Firebase->Auth. Firebase Auth streamlines integration with some the more popular Identity platforms, we will be integrating with Google+.
- Head over to the Firebase Console, if you have not already created an account, create one.
- After you have authenticated to google and land on the main firebase console, click on Create App and you will be presented with the dialog below, provide braindrain as the application name and click ‘Ok’. Enable the SIGN-IN methods for Google.
- Select the new application and choose Auth from the left hand navigation and SIGN-IN METHOD on the right-hand side and enable Google as a sign-in method. Follow the link to configure Google sign-in with the androiddebugkey and finally add the SHA1 fingerprint to your project settings in the Firebase console as described Clicking here.
- Now, download the google-service.json to the “app” folder of your solution.
Build your project and ensure the integration of Firebase-Auth is on track.
Encapsulating PubNub and Firebase
We will be using a number of third-party libraries to help realize Brain Drain. Some of these dependencies need to be added at the project level and some to the module build.gradle file. Ensure you place the dependencies in the correct build file.
Let’s start with the project level build.gradle file. We will need to add the apt.
As you can see we simply need to add the google-services plugin to the top level build file. This will allow google-services to be used by our module(s). The other two dependencies are for gradle and ButterKnife a simple, lightweight IOC container.
Now let’s transistion to the module level dependencies. Because we use Firebase-Auth to authenticate against Google, we need to have google-services and firebase-auth in the dependency in our module. The other dependencies in the list are for infrastructure concerns like JSON serialization or graphics.
Defining the dataStream
Our app will expose a real-time canvas that collaborators can use to draw diagram’ish drawings. To do this, we will listen and capture touch events via a custom view implementation. In a true extreme approach to development, we introduce these components just to get a build and some basic functionality. In the next part, I push these components to a more robust state of functionality (shapes and selectables) and finally add some social features with presence and state and persistence. The concepts presented here should be easily extendable and I encourage you, to do just that.
To start with, we need to define the over-the-wire protocol that can represent data that needs to be sent:
- The collaborator publishing the canvas events.
- The x,y location of the point in world space.
- The stroke data used to render the point on the canvas.
On the receiving end we will need to track all collaborators subscribed to a board and aggregate all packets, merge them together and draw our virtual board.
To capture this data we will use the following domain objects:
To ensure we maintain proper seperation of concern, Its important to encapsulate our interactions with Firebase and PubNub, This will ensure we can augment these interactions in futrue post in a predictable way. For this purpose I have created a singleton called CollaborationManager.
Create the Android Activities
We will be adding a few android activities throughout this series. First up is our MainActivity, the one the user will draw on and the SingInActivity. Both of these activities are fairly simple for now. The MainActivity is our launcher activity, when the onStart lifecycle method is invoked, it checks to see if we have an authenticated user, if not it will launch the SignInActivity. The SignInActivity will present the Google+ authentication button, and once clicked, the user will be authenticated and returned to the MainActivity. Concurrently the Firebase AuthListener will be triggered that is initialized and registered in the CollaborationManager. The AuthListener will then set the active user, initialize a connection to PubNub and we can then send/receive points.
As you can see, a check is made when the activity starts to determine if we have an authenticated user, if not the startIntent call then launches the SignInActivity, where the user can authenticate against Google, logging the user into the application.
Next we are using Firebase Remote Config to inject our PubNub subscribe/publish key into our application. The initAuthListener registers an auth listener with FirebaseAuth. The listener implementation waits for a user to authenticate and then initializes PubNub with the Firebase user ID. At this point we can identify each user after they authenticate for the first time.
Creating a Board
When a user authenticates to BrainDrain, we will next present a list of boards the user can join. To do this we will rely on the persistence capabilities of Firebase and store a simple list of static boards for now, in part two of this series we will add the ability to create and manage boards. For now we will simply display a list in a floating dialog and capture a select event to create the board. The select event will make a call to CollaborationManager, subscribeToBoard.
As you can see we made a call to subscribeToBoard which in turn subscribes to a board and is ready to receive geometry from each collaborator. Notice, the board is actually a channel group. Each collaborator will publish to an individual channel and the board simply aggregates and renders that content.
In this post we have covered a lot of ground, yet there is much to do. We now have a container that authenticates users, an over the wire protocol for capturing real-time whiteboard collaborations. My next post in this series will add the graphical elements and ability to cast your board to screen near you. We will wrap up this series by adding social features using the PubNub presence API in conjunction with Firebase-Auth to make finding users to collaborate with a snap.