Skip to content

Most visited

Recently visited


Implement a Companion App

This lesson teaches you to

  1. Create a companion app module
  2. Display database entries with Firebase UI

Try it out

A smart doorbell might or might not have a local display. A companion app for mobile devices allows the user to remotely and automatically see new doorbell ring events and images as they are inserted by the embedded device.

In this lesson, you will build an Android mobile app that is synchronized with the doorbell events in Firebase.

Create a companion app module

Create a second module in your project alongside the embedded doorbell app. To create a new project module:

  1. Follow the instructions to Create a New Module in Android Studio.
  2. Create a new Phone & Tablet Module.

Add Firebase to your module

To enable the Firebase APIs for your companion app module:

  1. Install the Firebase Android SDK into your app module.
  2. Download and install the google-services.json file as described in the instructions.

  3. Add the Firebase Realtime Database and Storage dependencies to your app-level build.gradle file:

    dependencies {
        implementation ''
        implementation ''
        implementation ''

Add FirebaseUI and Glide to your module

The FirebaseUI-Android library provides a collection of UI components to quickly bind data coming from the Firebase APIs:

To enable FirebaseUI and Glide for your companion app module:

  1. Add the FirebaseUI-Android and Glide dependencies to your app-level build.gradle file:

    dependencies {
        implementation 'com.firebaseui:firebase-ui-database:3.2.1'
        implementation 'com.firebaseui:firebase-ui-storage:3.2.1'
        implementation 'com.github.bumptech.glide:glide:4.6.1'
        annotationProcessor 'com.github.bumptech.glide:compiler:4.6.1'
  2. Create a FirebaseGlideModule class to enable the FirebaseImageLoader to fetch images from Firebase Storage and display them through Glide:

    public class FirebaseGlideModule extends AppGlideModule {
        public void registerComponents(Context context, Glide glide, Registry registry) {
            // Register FirebaseImageLoader to handle StorageReference
            registry.append(StorageReference.class, InputStream.class,
                    new FirebaseImageLoader.Factory());

Display doorbell events with FirebaseUI

To simplify the interaction with the Firebase data model, create a DoorbellEntry model class that contains the same properties described in the JSON schema.

public class DoorbellEntry {

    Long timestamp;
    String image;
    Map<String, Float> annotations;

    public DoorbellEntry() {

    public DoorbellEntry(Long timestamp, String image, Map<String, Float> annotations) {
        this.timestamp = timestamp;
        this.image = image;
        this.annotations = annotations;

    public Long getTimestamp() {
        return timestamp;

    public String getImage() {
        return image;

    public Map<String, Float> getAnnotations() {
        return annotations;

Create a FirebaseRecyclerAdapter instance to bind the data from each Firebase event to the UI. The FirebaseUI library automatically handles creation of each DoorbellEntry model object from the Firebase database. Connect the image, timestamp, and annotations to the view in the onBindViewHolder() method:

public class DoorbellEntryAdapter extends
        FirebaseRecyclerAdapter<DoorbellEntry, DoorbellEntryAdapter.DoorbellEntryViewHolder> {

     * ViewHolder for each doorbell entry
    public static class DoorbellEntryViewHolder extends RecyclerView.ViewHolder {

        public final ImageView image;
        public final TextView time;
        public final TextView metadata;

        public DoorbellEntryViewHolder(View itemView) {

            this.image = (ImageView) itemView.findViewById(;
            this.time = (TextView) itemView.findViewById(;
            this.metadata = (TextView) itemView.findViewById(;

    private Context mApplicationContext;
    private FirebaseStorage mFirebaseStorage;

    public DoorbellEntryAdapter(Context context, DatabaseReference ref) {
        super(new FirebaseRecyclerOptions.Builder<DoorbellEntry>()
                .setQuery(ref, DoorbellEntry.class)

        mApplicationContext = context.getApplicationContext();
        mFirebaseStorage = FirebaseStorage.getInstance();

    public DoorbellEntryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View entryView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.doorbell_entry, parent, false);

        return new DoorbellEntryViewHolder(entryView);

    protected void onBindViewHolder(DoorbellEntryViewHolder holder, int position, DoorbellEntry model) {
        // Display the timestamp
        CharSequence prettyTime = DateUtils.getRelativeDateTimeString(mApplicationContext,
                model.getTimestamp(), DateUtils.SECOND_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, 0);

        // Display the image
        if (model.getImage() != null) {
            StorageReference imageRef = mFirebaseStorage.getReferenceFromUrl(model.getImage());


        // Display the metadata
        if (model.getAnnotations() != null) {
            ArrayList<String> keywords = new ArrayList<>(model.getAnnotations().keySet());

            int limit = Math.min(keywords.size(), 3);
            holder.metadata.setText(TextUtils.join("\n", keywords.subList(0, limit)));
        } else {
            holder.metadata.setText("no annotations yet");

Create an instance of the DoorbellEntryAdapter and attach it to a RecyclerView in your activity. The FirebaseUI adapters automatically register event listeners with the database. To avoid memory leaks, use the startListening() and stopListening() methods of the adapter to ensure the listeners are only active while the activity is in the foreground.

public class MainActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private DoorbellEntryAdapter mAdapter;

    protected void onCreate(Bundle savedInstanceState) {

        // Reference for doorbell events from embedded device
        DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("logs");

        mRecyclerView = (RecyclerView) findViewById(;
        // Show most recent items at the top
        LinearLayoutManager layoutManager =
                new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true);

        // Initialize RecyclerView adapter
        mAdapter = new DoorbellEntryAdapter(this, ref);

    protected void onStart() {

        // Initialize Firebase listeners in adapter

    protected void onStop() {

        // Tear down Firebase listeners in adapter

Congratulations! You have built a cloud-enabled doorbell for the connected home using Android Things!

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields


Follow Google Developers on WeChat

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience. (Dec 2017 Android Platform & Tools Survey)