5 Comments

Creating a Custom Gboard Sticker Pack – A Guide for Android Newbies

Recently, Google started allowing developers to create custom sticker packs for Gboard on Android. In case you’ve never heard of Gboard, it’s Google’s latest virtual keyboard. Available on both iOS and Android, Gboard is an all-in-one keyboard that includes glide and voice typing, a predictive word engine, and stickers. It is also the built-in keyboard on my Pixel.

Digital stickers are curated expressive graphics used in various chat apps such as Facebook Messenger, Line, WeChat, Google Hangouts, and more. I think of them as giant emojis.

I am not a fan of Google’s sticker styles, and since I have a cute dog with tons of personality, I want to send stickers of her instead.

I had never programmed for Android before. There are a handful of existing tutorials for creating custom sticker packs, but they all assumed a level of Android development understanding that I lacked. Therefore, this guide is for an experienced developer, but a complete Android newbie.

How Gboard Stickers Work

Every chat app with stickers that I have used has some sort of sticker store where you can download sticker packs. Given that, I initially thought that using custom stickers meant simply handing Google a folder of properly size images. It turns out that is entirely incorrect.

Gboard isn’t a chat app; it’s a keyboard. Although it technically has a sticker store, it really just provides a list of apps that can add sticker packs to Gboard. Sticker packs aren’t “installed” on the keyboard. Instead, they are available for the keyboard to find via app indexing.

I suspect Google’s intended use case for custom stickers is branding (e.g. if you already have the Disney app, you can add Disney stickers), but it makes adding a stand-alone sticker pack a little strange. When I tried adding some custom sticker packs to my Gboard, they all involved installing an app that showed a preview of the stickers and a button to actually install the stickers into Gboard.

Prerequisites

1. An Android device

You will need an Android device with Gboard installed to actually use your custom sticker pack.

Technically, the device needs to be running Android 4.0 or newer and to have a Google Play services version of 11.4.0 or higher, but that covers practically any Android phone you may have that is still running.

2. A Google account

Since you’re programming for a Google keyboard on Android, I assume you know that you need a Google account. If you don’t have one for some reason, you will need to create one.

3. Firebase & App Indexing

Firebase is a Google brand that encompasses various mobile development tools. You should be able to sign in with your Google account.

The particular tool used for custom stickers is App Indexing. In general, indexing involves building a list (index) that can be searched. In traditional search engine indexing, the search engine finds websites and adds them to the index so it can display them in search results.

Firebase’s App Indexing allows you to add your app to Google’s search engine index so it can be shown alongside the search results.

Furthermore, personal content indexing creates an on-device index that integrates with the phone’s Google search. This allows you to type a friend’s name in the search bar and see their contact information appear alongside search results.

I believe Gboard looks for the sticker images in the on-device index, which is why the only way to add stickers to the phone is by indexing them. To do this, we need Firebase.

4. Android Studio

Any Android programming requires Android Studio, so you’ll need to download and install it.

5. Images

Finally, you’ll need actual images to use as stickers. Google suggests square images of 320 or 500 px.

Getting Started

1. Set up an Android project

Start by opening Android Studio and clicking Start a new Android Studio project.

The only parts of the setup that I changed were the Application name, Project location, and selecting Empty Activity to start.

2. Set up a Firebase project

Automatic

The latest version of Android Studio has deep integration with Firebase. Make sure you log into your Google account with Android Studio.

Go to Tools -> Firebase to bring up the Firebase assistant panel.

Open the App Indexing section and click Get Started with App Indexing.

For custom stickers, you only need to do the first two actions: Connect your app to Firebase and Add App Indexing library to your app.

If you want to link to an existing Firebase or Google project, feel free to use that. I let Android Studio create a new Firebase project for me. Whichever you decide, clicking Connect to Firebase will close the modal and do some background work. See the manual setup below for more details on what Android Studio is doing for you.

The second step brings up another modal that simply informs you of what it will modify in your project.

Again, the manual setup below follows the exact same process that Android Studio will automate for you, so you can simply click Accept Changes.

At this point, Firebase should be all integrated with your app, so you can move on to index the stickers.

Manual

Go to the Firebase console and Add a project. I only set Project name, but you can modify Google Analytics permissions if you want.

In the left drawer, click on the gear and go to Project settings.

Scroll down and click Add Firebase to your Android app.

Follow the instructions on the modal, and you should be done with Firebase. The key step here is to get the google-services.json file from Firebase integrated into your Android project to index the stickers.

3. Index the stickers

We are now ready to write the actual code that indexes the stickers.

Create a new subclass of JobIntentService in the same directory as MainActivity. I named mine StickerIndexingService.

JobIntentService

For those new to Android programming, the JobIntentService is an abstract class for handling work asynchronously. Work can be queued via the static method enqueueWork. When the system is ready, it will call onHandleWork.

You define the work to be done by extending this JobIntentService as we have done and implementing onHandleWork.

AndroidManifest.xml

You probably notice our new StickerIndexingService class name is highlighted. Hovering over it, you see a message about the service not being registered in the manifest.

The AndroidManifest.xml describes your app to the build tools, the Android operating system, and the Play store. There is a list of specific components that need to be included in the manifest so the Android system knows how to start them. StickerIndexingService is one such component.

Edit your AndroidManifest.xml to include the following service tag:

    ...
    <application
        ...>
        <activity android:name=".MainActivity">
            ...
        </activity>
        <service android:name=".StickerIndexingService"
            android:permission="android.permission.BIND_JOB_SERVICE" />
    </application>
    ...

StickerBuilder and StickerPackBuilder

Android indexes things via an indexable instance. If you want to index a music album or a book, you would need to build an appropriate indexable via an IndexableBuilder. Android provides some convenience builders, including StickerBuilder and StickerPackBuilder.

Create your first sticker Indexable and sticker pack Indexable by adding the following to onHandleWork.

StickerBuilder smileyStickerBuilder = Indexables.stickerBuilder()
        .setName("smiley")
        .setUrl("mystickers://sticker/smiley")
        .setImage(smileyImageUri)
        .setDescription("Melissa smiling!")
        .setKeywords("Melissa", "smiley")
        .setIsPartOf(Indexables.stickerPackBuilder().setName("Melissa"));

StickerPackBuilder stickerPackBuilder = Indexables.stickerPackBuilder()
        .setName("Melissa")
        .setUrl("mystickers://sticker/pack/melissa")
        .setHasSticker(smileyStickerBuilder);

You may notice that the sticker pack and sticker circularly reference each other via isPartOf and hasSticker. It is not entirely clear whether both are needed, but Google’s own sample app is using both isPartOf and hasSticker.

Image assets

Now we need to define a URI pointing to the actual image asset.

In Google’s sample app, the stickers are square, 400px, dynamically generated, colored blocks saved to local storage. In other example apps, the images are saved in Firebase storage and are publicly available.

I chose to embed the images in the app itself. I believe Google does not vary the asset sizes for their Gboard stickers1, so I added my square, 500px png to the res/drawable directory.

The correct URI string to reference an image asset in the drawable folder turns out to be android.resource://com.vicros0723.melissastickers/drawable/smiley.
Add the following line above the previous Indexable builder code.

String smileyImageUri = Uri.parse("android.resource://com.vicros0723.melissastickers/drawable/smiley").toString();

FirebaseAppIndex

An instance of FirebaseAppIndex actually indexes these Indexables.

Add the following after the Indexable builder code from above

Task<Void> update = FirebaseAppIndex.getInstance().update(
        stickerPackBuilder.build(),
        smileyStickerBuilder.build());

AndroidManifest.xml

The Android system needs to know how to handle the deep link URLs for the sticker and sticker pack. To do this, we’ll add an Intent-Filter to our AndroidManifest.xml.

Edit your AndroidManifest.xml to look like the following:

        ...
        <activity android:name=".MainActivity">
            ...
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="mystickers" />
                <data android:host="sticker" />
            </intent-filter>
        </activity>
        ...

4. Enqueue the job

The last step is simply to kick off the StickerIndexingService. Enqueueing the job is not sticker-specific, so I simply enqueued the job as soon as the app loads.

I wrapped the JobIntentService static method, enqueueWork, in StickerIndexingService.

    private static final int UNIQUE_JOB_ID = 42;

    public static void enqueueWork(Context context) {
        enqueueWork(context, StickerIndexingService.class, UNIQUE_JOB_ID, new Intent());
    }

and called it from MainActivity as follows:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        StickerIndexingService.enqueueWork(MainActivity.this);
        setContentView(R.layout.activity_main);
    }

This is definitely not the recommended long-term method, and you can see alternatives in the other tutorials.

Test

At this point, you should be able to test this app. I never figured out how to run this on an Android emulator, so plug your Android device into your computer and run the app with or without debug.

Now when you use Gboard, you should see your new sticker pack and be able to send stickers!

Other Resources

1If you are planning to use the images for other situations such as showing the user a preview and allowing the user to share the sticker from within your app, you will need to provide a variety of sizes for your image. I suggest checking out the example app by Playground Inc. for guidance on how to do that.