Cannonball Android Tutorial

We created Fabric to make it easy to take your app to the next level, from awesome idea to fully fledged business. As app developers ourselves, we wanted to make it simple and delightful to build with Fabric, while also solving the biggest challenges mobile developers face when building mobile apps: stability, distribution, revenue and identity.

To showcase our mobile platform and highlight some of the features provided by each Kit, we created a sample app called Cannonball. Cannonball for Android is available on the Play Store but also open sourced on GitHub.

We love games, so we decided to bring to mobile one of our favorite games from the office, magnetic poetry. If you’re not familiar with magnetic poetry, it’s a game where you get a particular set of words that you stick on the fridge, and you can start arranging them to compose stories.

In this tutorial, you will learn how we integrated Crashlytics, Twitter, and Digits Kits in the Cannonball app for Android, and which benefits we got by using Fabric along the way, from building to shipping the app.

Note

We also built Cannonball for iOS. It’s available on the App Store and open sourced on GitHub. You can also read the tutorial to build Cannonball for iOS with Fabric and Swift.

Overview

With Cannonball, we wanted to take the very simple idea of magnetic poetry and bring it to mobile.

We started off with a way to pick different themes and sample words to build your poem. We also added pictures and a timer to make the game a bit more exciting. You can swipe through to select the picture and start tapping on the words to compose a poem.

In order to provide a seamless experience on mobile and a way to carry data from one device to another, we always need an idea of users and login, which is why we decided here to bring a way to sign in using Twitter or their phone number with Digits.

Users can keep a history of the poems they created, share their best stories to Twitter and enjoy poems that have been shared on Twitter.

../../_images/cannonball-overview.png

From left to right: Sign In, Theme Chooser, Poem Builder and Poem History screens.

How to Follow This Tutorial

If you want take a look at the app before reading the tutorial, you can install Cannonball from the Play Store.

Install Cannonball for Android Clone GitHub repository

Get the Code, Setup Your IDE and the Fabric Plugin

The best way is to start by cloning the repository and following the instructions on GitHub. This approach is particularly important if you want to see how Fabric dashboards such as Crash Reporting, Beta or Answers work. Remember that you are going to need an account on fabric.io to do that.

Download the Fabric plugin for your favorite IDE and open the Cannonball app in Android Studio or IntelliJ.

App Structure

The app has seven Activities under the io.fabric.samples.cannonball.activity package:

AboutActivity About screen with instructions and Sign Out button
InitialActivity Hidden screen used to redirect user to Login or the Theme Chooser
LoginActivity Sign In with Twitter and Digits buttons
PoemBuilderActivity UI to build a poem
PoemHistoryActivity List of poems
PoemPopularActivity Twitter timeline with previously shared poems
ThemeChooserActivity UI to choose a theme to build a poem and access other features

Please note that for simplicity, we don’t have a backend linked to this sample app. We only store some data locally in a SQLite database accessed via a ContentProvider (see db package). Personal data from Sign In with Twitter and Digits are saved in SharedPreferences via the SessionRecorder class.

While we don’t cover them in detail in this tutorial, you can also see in Cannonball examples of Android functionalities including drag and drop UI elements, custom views and generating images from views in the code base.

1. Initializing Fabric

By using our Fabric IDE plugin, we can easily install Fabric dependencies and also automatically apply the code to initialize Fabric in our app. Otherwise, we can manually integrate Fabric in our app.

Fabric SDKs separates functionality into modules called Kits and we should indicate in the code which ones we want to use. Fabric.with() is how we wire up individual Kits from Fabric. This is typically done by extending Android’s Application class. Let’s see how we do that in our code:

Note

We are going to cover all the Kits in this tutorial, so feel free to onboard them all right now to save a few seconds as we did in the snippet below. You can also add them one by one once required and the Fabric plugin will invite you to add them here to Fabric.with(). Of course, depending on which app you are building, you would only pick and choose the Kits you need.

public void onCreate() {
    super.onCreate();
    singleton = this;
    extractAvenir();

    authConfig = new TwitterAuthConfig(BuildConfig.CONSUMER_KEY, BuildConfig.CONSUMER_SECRET);
    Fabric.with(this, new Crashlytics(), new Digits(), new Twitter(authConfig));

    Crashlytics.setBool(CRASHLYTICS_KEY_CRASHES, areCrashesEnabled());
}

2. Making Cannonball Stable with Crashlytics

All mobile apps crash. It’s nothing to be ashamed of, but what’s important is to be proactive about fixing these crashes. We cannot rely exclusively on local environments and devices to test an app and ensure it has no bugs. Stability is critical in mobile apps. Any bad experiences for users can lead to uninstalls and poor reviews.

That’s where Fabric comes in. Crashlytics is here to help us build the best apps and making sure they’re stable and reliable.

Integrating the Crashlytics Kit takes only one line of code. After clicking “Install” on Crashlytics in the Fabric plugin, all we need to do is to include Crashlytics in Fabric.with(), and we already just did that!

public void onCreate() {
    super.onCreate();
    singleton = this;
    extractAvenir();

    authConfig = new TwitterAuthConfig(BuildConfig.CONSUMER_KEY, BuildConfig.CONSUMER_SECRET);
    Fabric.with(this, new Crashlytics(), new Digits(), new Twitter(authConfig));

    Crashlytics.setBool(CRASHLYTICS_KEY_CRASHES, areCrashesEnabled());
}

With Crashlytics enabled, the next time the app crashes, we’ll get a full report with the files and specific lines that caused the crash.

../../_images/crashlytics-crash-reporting.png

Crash reporting dashboard for the Cannonball app.

We’ll also get all the details on the individual devices that experienced the crashes. So we’re equipped to understand and solve any problems.

Optionally, we can also add more context to crash reports by using Custom Logging and Custom Keys. We instrumented the Cannonball code to add more context where it was relevant so our code contains several occurrences of that with for instance Crashlytics.log and Crashlytics.setBool.

We also created a class named SessionRecorder to track user sessions. As you can see below, we added a way to tie user information directly to crash reports to help debug issues with Crashlytics.setUserIdentifier as well.

private static void recordSessionState(String message,
                                       String userIdentifier,
                                       boolean active) {
    Crashlytics.log(message);
    Crashlytics.setUserIdentifier(userIdentifier);
    Crashlytics.setBool(App.CRASHLYTICS_KEY_SESSION_ACTIVATED, active);
}

3. Sharing Content to Twitter

Now that our app will be more stable thanks to Crashlytics, we want to make the game more fun! The cool thing about magnetic poetry is that some friends or colleagues would come by, rearrange some words on the fridge and leave a poem for us to discover.

In the context of a mobile app, once a user has created a poem, it would be great to share it with their friends. By sharing content to Twitter, users actually play a huge role in app marketing since these Tweets can have high engagements and drive organic app installs.

The Twitter Kit includes a TweetComposer which makes this task really simple. Let’s see the code which is triggered when the user taps the “Share” button to tweet a poem from the history screen:

final TweetComposer.Builder builder
    = new TweetComposer.Builder(PoemHistoryActivity.this)
    .text(getApplicationContext().getResources().getString(R.string.share_poem_tweet_text) + " " + hashtag)
    .image(imageUri);
builder.show();

All we need to do is set the suggested Tweet text, automatically appending the relevant hashtags like #cannonballapp, and attach the poem image. This is done by using the Builder class from TweetComposer. After setting these attributes we call builder.show() method to execute the action. On Android, TweetComposer creates an intent that will use the Twitter app to share this content. In case the Twitter app is not installed, the Web version of that composer will be loaded.

../../_images/cannonball-twitter-sharing.png

Sharing a poem from Cannonball to Twitter.

4. Embedding Tweets in the App

Users can now share poems to Twitter, but that’s not all we want to do. What if we’re taking all the awesome poems people have shared and bringing this content back into Cannonball so users can enjoy them without leaving the app?

The Twitter Kit enables us to embed public, real-time, relevant Tweets to complement the content in our app and make our app more engaging.

There are a handful of features the Twitter Kit offers when it comes to bringing Twitter content inside mobile apps. We can natively embed Tweets in different formats but also complete Timelines, as well as interacting with the Twitter API.

People are sharing Tweets with the hashtags #cannonballapp along with the theme and a picture, so what we need for Cannonball is to bring in Tweets which match the keywords below:

private static final String SEARCH_QUERY = "#cannonballapp AND pic.twitter.com AND " +
        "(#adventure OR #nature OR #romance OR #mystery)";

This is a complex search query with operators that we can type in any Twitter app or using the Twitter Search API.

Now we just need to implement a native Timeline of Tweets matching this query. The Twitter Kit provides different kinds of Timelines. The SearchTimeline class is exactly what we need.

private void setUpPopularList() {
     SearchTimeline searchTimeline = new SearchTimeline.Builder().query(SEARCH_QUERY).build();

     final TweetTimelineListAdapter timelineAdapter = new TweetTimelineListAdapter(this, searchTimeline);
     setListAdapter(timelineAdapter);
     getListView().setEmptyView(findViewById(R.id.loading));
}

The SearchTimeline takes care of everything, from wrapping the API requests to handling the JSON responses so we don’t have to worry about that. To display Tweets properly, we attach the instance to TweetTimelineListAdapter, a class that will display the displays Tweets in a way that is compliant with Twitter’s Display Guidelines and keeps the user experience consistent across all apps. We provide the ability to change the look and feel of these Tweets, for instance if an app has a dark background.

Here is a screenshot of the Poem Timeline displayed in Cannonball:

../../_images/cannonball-twitter-timeline.png

Twitter Timeline showing recently shared poems on Cannonball.

Note

The Twitter Kit requires a Twitter API key. When using the Fabric plugin and installing the Twitter Kit, a Twitter app is automatically generated with guest authentication enabled. If you would like to use an existing Twitter app instead, please take a look at the Twitter Kit advanced setup documentation page.

5. Distributing Beta Builds and Getting Feedback

There is another great tool that Crashlytics provides during a development process. After spending time implementing a new app or new features, it is very important to distribute this app to a larger group in order to make sure it works well and get feedback before releasing it to the App Store.

Crashlytics provides a solution called Beta. This allows us to easily distribute beta builds of our app.

All we need to do is simply drag and drop an APK to the Fabric plugin in your IDE, entering some email addresses, and in a matter of minutes, we will start having some testers. Then we can follow every beta tester status on the Fabric dashboard.

../../_images/crashlytics-beta.png

Dashboad showing the status of every tester in Beta by Crashlytics.

6. Sign In with Digits and Twitter

In order to support any features which rely on identifying a user, such as syncing data to several devices or personalization, Fabric provides two options respectively in the Twitter Kit and the Digits Kit: Sign In with Twitter and Digits.

../../_images/cannonball-sign-in.png

Cannonball Sign In with Twitter and Digits.

Sign In with Digits

Digits lets people create an account or sign into any mobile apps using nothing but their phone number, allowing them to forget about passwords. This service is built on the exact same global and reliable infrastructure Twitter uses to send SMS messages. Digits verifies the user’s phone number via SMS, provides a simple and customizable user interface, and is completely free.

For convenience, Digits provides a button to start the login process that we can quickly embed in our app via the Fabric plugin, but it also allows us to use our own button and customize the entire user experience. This is great to make Digits match our app look and feel and make it completely part of our app.

Cannonball leverages Digits to offer a simple, elegant, and mobile-friendly way to login. To add phone number sign in to Cannonball, we first add a Digits button to our layout file (in this case, it’s an extension to DigitsAuthButton to be able to customize the button UI):

<io.fabric.samples.cannonball.view.CannonballDigitsAuthButton
    android:id="@+id/phone_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/sign_up_button"
    android:layout_marginTop="@dimen/login_button_margin_top"
    android:layout_marginBottom="@dimen/login_button_margin_bottom"
    android:layout_marginLeft="@dimen/login_button_margin_start"
    android:layout_marginStart="@dimen/login_button_margin_start"
    android:layout_marginRight="@dimen/login_button_margin_end"
    android:layout_marginEnd="@dimen/login_button_margin_end"
    android:text="@string/sign_in_with_phone"
    android:layout_below="@id/twitter_button"
    android:textColor="@color/grayish_blue"
    android:textSize="@dimen/login_button_text_size"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:layout_alignParentRight="true"
    android:layout_alignParentEnd="true" />

And then we implement the button behavior in the LoginActivity.

The method below is called during execution of onCreate() and it instantiates a DigitsAuthButton class. Also, to customize not only the button but also the entire experience, Digits supports theming. This object has a setAuthTheme() method to set the Digits theme so we can provide a consistent design.

We also need to set two callback methods: success and failure. When the user is able to confirm their identity using their phone number, the success callback will let us access a DigitsSession object, containing a stable, unique ID as well as the verified phone number in phoneNumber.

private void setUpDigitsButton() {
    phoneButton = (DigitsAuthButton) findViewById(R.id.phone_button);
    phoneButton.setAuthTheme(R.style.AppTheme);
    phoneButton.setCallback(new AuthCallback() {
        @Override
        public void success(DigitsSession digitsSession, String phoneNumber) {
            SessionRecorder.recordSessionActive("Login: digits account active", digitsSession);
            Answers.getInstance().logEvent("login:digits:success");
            startThemeChooser();
        }

        @Override
        public void failure(DigitsException e) {
            Answers.getInstance().logEvent("login:digits:failure");
            Toast.makeText(getApplicationContext(),
                    getResources().getString(R.string.toast_twitter_digits_fail),
                    Toast.LENGTH_SHORT).show();
            Crashlytics.logException(e);
        }
    });
}

In the screenshot below we can see the main Digits screen triggered when the user taps the “Use my phone number” button. After typing their phone number, the user will get an SMS message with a confirmation code to enter on the next screen. Once the authentication is successful, the user is redirected back to app and it is then possible to access and save the user information.

../../_images/cannonball-digits.png

Digits inviting the user to enter their phone number.

Sign In with Twitter

In Cannonball we also added a way to login with Twitter. Sign In with Twitter enables an app to personalize the experience based on the information from Twitter by doing API calls on behalf of the user to retrieve their profile, Tweets or followers for instance.

The integration is very similar to Digits as it involves adding a button, instantiating it and implementing the completion. The great advantage of using the Twitter Kit is that we don’t need to worry about the complexity of OAuth 1.0a in our app or handling JSON structures returned by the Twitter API.

We start the integration by adding a TwitterLoginButton view to our layout (we will skip since it’s very similar to what we did for Digits above). Then we instantiate the button in the LoginActivity:

private void setUpTwitterButton() {
    twitterButton = (TwitterLoginButton) findViewById(R.id.twitter_button);
    twitterButton.setCallback(new Callback<TwitterSession>() {
        @Override
        public void success(Result<TwitterSession> result) {
            SessionRecorder.recordSessionActive("Login: twitter account active", result.data);
            Answers.getInstance().logEvent("login:twitter:success");
            startThemeChooser();
        }

        @Override
        public void failure(TwitterException exception) {
            Answers.getInstance().logEvent("login:twitter:failure");
            Toast.makeText(getApplicationContext(),
                    getResources().getString(R.string.toast_twitter_signin_fail),
                    Toast.LENGTH_SHORT).show();
            Crashlytics.logException(exception);
        }
    });
}

In this case, the success callback will return a TwitterSession object that will provide us with the information about the user who just logged into our app.

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    twitterButton.onActivityResult(requestCode, resultCode, data);
}

Now when the user clicks on the “Sign In with Twitter” button, Cannonball automatically invites the user to grant the Twitter account permission:

../../_images/cannonball-twitter-accounts-access.png

Cannonball app requesting access to Twitter accounts.

7. Understanding Growth with Answers

When setting up Crashlytics, we also get access to Answers, a real-time analytics product that will help us understand the key growth and retention metrics for our app, with no impact on performance.

Answers puts the most important metrics front and center including daily active users, month active users, crash-free users thanks to its relationship with crash reporting, and will also proactively highlight any metrics that should get our attention, all this without any configuration. Here is what the Answers dashboard looks like for Cannonball:

../../_images/answers-dashboard.png

Answers dashboard for Cannonball.

With Answers, we can also track specific actions by instrumenting our code with Custom Events sent to Answers. We added Custom Events in the Cannonball code when users are interacting with some of the features, for instance when they finish composing a poem or stop creating one, or when they are viewing their history or popular poems. Below is an example:

Answers.getInstance().logCustom(new CustomEvent("clicked save poem")
        .putCustomAttribute("poem size", poemText.length())
        .putCustomAttribute("poem image", poemImage)
        .putCustomAttribute("poem theme", poemTheme.getDisplayName()));

A CustomEvent can be created with just an event name or with custom attributes that will be tracked as well, like we did in the above snippet, giving better insights on how users are interacting with this feature. The dashboard will show all the metrics around this event:

../../_images/answers-custom-events.png

Answers dashboard on the Custom Event triggered after composing a poem.

Recap

We built the Cannonball app to show how easy it is to integrate all Fabric Kits in a mobile app.

First, we improved our app stability, gathered user feedback and had deep insights thanks to the Crashlytics Kit that provides crash reporting, beta distributions, and metrics via Answers.

Using the Twitter Kit, we were able to make our game more social, let our users share poems but also browse popular poems others have shared on Twitter, bringing real-time content inside the game.

Lastly, we provided very simple ways to sign in, in particular with the Digits Kit which enables a beautiful and seamless phone number sign in for users, with no passwords to remember.

If you want to dive into Fabric even more, browse our documentation or find us on Stack Overflow. We can’t wait to see the great apps you are going to build with Fabric.