Following Google I / O 2016 - New Firebase: Android Integration

    Hello, Habr! We continue the series of articles on the technologies presented at our annual event Google I / O. Today our guest is Alexander Denisov, and he will talk about his acquaintance with the basic features of the updated Firebase.



    Hi, I am Alexander Denisov, work for Netcracker as a Senior Software Developer and head the GDG of Nizhny Novgorod. I have been following the technologies that Google is developing for a long time, and this time I just could not get past Firebase. There are many changes, they are conceptual in themselves, and, finally, they turn Firebase itself into one big service for building a mobile backend.

    I was very impressed with the presentation, so I decided to personally try to work with new features and share with you how to take the first steps into this amazing world of convenient and fast development for Android.

    Background


    The joint history of Google and Firebase began back in 2014: then the entire Firebase team simply switched to Google and continued to work on their product, gaining access to the cloud capabilities of the Google Cloud Platform and other Google technologies. In those days, Firebase was, in fact, a set of REST services for managing a cloud NoSQL database designed to store and synchronize data between several clients, and related authentication and hosting services. It looked something like this:



    In 2016, the jubilee tenth Google I / O takes place, and it presents the new features of Firebase. He grew, matured and expanded to a set of 15 products designed not only for development, but also to facilitate the promotion and monetization of applications. These products can be used either individually or in any combination - depending on your needs.



    In a good way, each of these products should be dedicated to a separate article, with lots of examples and codes. But this takes time, so while we just try to create a small application based on Firebase, get acquainted with some (the most interesting, in my opinion) possibilities and just try to feel the “taste” of the technology ... As an example, I will create a simple chat, messages and information about the interlocutors which will be stored in the cloud. You won’t believe how easy it was to develop such things.

    1. Firebase - the first steps


    Preparatory work, registration and authorization


    Create a simple project (let's call it com.google.simplefirebasechat) with empty markup. A start has been made, now we need to associate it with Firebase. To do this, go to the main tool for managing Firebase - the console and click "Create a new project":



    Since we are going to work with the Android application, we must choose it from the proposed options.



    The system will prompt us to indicate the name of our application and a security key of SHA1 format





    Note: If you ugly forget what key you have, open the Windows console (Win + R, CMD, Enter) and extract it from debug.keystore. To do this, enter the following commands:

    keytool -exportcert -alias androiddebugkey -keystore %USERPROFILE%/.android/debug.keystore -list -v
    


    And log in with a password from your debug repository, (default is “android” )



    After you provide all the necessary data, the browser will automatically download the generated json Firebase configuration json with the metadata needed to work. The resulting file should be put in the app directory of our application. Now in the build.gradle of the project level, you need to add a dependency on the corresponding library
    classpath 'com.google.gms:google-services:3.0.0'


    You should also append the following line to the end of build.gradle in the app directory
    apply plugin: 'com.google.gms.google-services'


    which connects to the Google Services project the plugin necessary for processing configuration JSONa and connecting the main com.google.gms: google-services libraries.

    Voila! Firebase is associated with an application that you can try to build!

    Checking Firebase Performance


    The next step is to verify that everything is in order, and we really connected. To do this, we will add a couple of records to the database, and then read them in the application.

    Open the Firebase console, look for the Database tab, and create a simple JSON for storing messages in it. The console interface makes this easy to do.



    Now we’ll read the entered data, requesting it from our application ... In order to access it and be able to display it in our interface, we will add several corresponding dependencies to build.gradle
    compile 'com.google.firebase:firebase-database:9.0.0'
    compile 'com.firebaseui:firebase-ui-database:0.4.0'
    compile 'de.hdodenhof:circleimageview:1.3.0'
    compile 'com.github.bumptech.glide:glide:3.6.1'


    We will also add a new class for the chat message and markup to display it. With the necessary preparations completed, now we are going to display the content received from the database.
    To do this, we will need to create the main activity and place interface elements on it that will help us test the operation of Firebase. We add a field for entering a message, a Send button, a ProgressBar to load data and a RecyclerView to display data from the database, and add a couple of lines of code to the class itself.

    So, what did we get:
    package com.google.simplefirechat;
    import android.content.SharedPreferences;
    import android.preference.PreferenceManager;
    import android.support.v4.content.ContextCompat;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.text.Editable;
    import android.text.InputFilter;
    import android.text.TextWatcher;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.ProgressBar;
    import android.widget.TextView;
    import com.google.android.gms.common.api.GoogleApiClient;
    import com.google.firebase.database.DatabaseReference;
    import com.firebase.ui.database.FirebaseRecyclerAdapter;
    import com.google.firebase.database.FirebaseDatabase;
    import de.hdodenhof.circleimageview.CircleImageView;
    import com.bumptech.glide.Glide;
    public class MainActivity extends AppCompatActivity {
       private DatabaseReference mSimpleFirechatDatabaseReference;
       private FirebaseRecyclerAdapter
               mFirebaseAdapter;
       private RecyclerView mMessageRecyclerView;
       private LinearLayoutManager mLinearLayoutManager;
       private ProgressBar mProgressBar;
       public static class FirechatMsgViewHolder extends RecyclerView.ViewHolder {
           public TextView msgTextView;
           public TextView userTextView;
           public CircleImageView userImageView;
           public FirechatMsgViewHolder(View v) {
               super(v);
               msgTextView = (TextView) itemView.findViewById(R.id.msgTextView);
               userTextView = (TextView) itemView.findViewById(R.id.userTextView);
             userImageView = (CircleImageView) itemView.findViewById(R.id.userImageView);
           }
       }
       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
           mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
           mMessageRecyclerView = (RecyclerView) findViewById(R.id.messageRecyclerView);
           mLinearLayoutManager = new LinearLayoutManager(this);
           mLinearLayoutManager.setStackFromEnd(true);
           mMessageRecyclerView.setLayoutManager(mLinearLayoutManager);
           mSimpleFirechatDatabaseReference = FirebaseDatabase.getInstance().getReference();
           mFirebaseAdapter = new FirebaseRecyclerAdapter(
                   ChatMessage.class,
                   R.layout.chat_message,
                   FirechatMsgViewHolder.class,
                   mSimpleFirechatDatabaseReference.child("messages")) {
               @Override
               protected void populateViewHolder(FirechatMsgViewHolder viewHolder, ChatMessage friendlyMessage, int position) {
                   mProgressBar.setVisibility(ProgressBar.INVISIBLE);
                   viewHolder.msgTextView.setText(friendlyMessage.getText());
                   viewHolder.userTextView.setText(friendlyMessage.getName());
                   if (friendlyMessage.getPhotoUrl() == null) {
                       viewHolder.userImageView
                               .setImageDrawable(ContextCompat
                                       .getDrawable(MainActivity.this,
                                               R.drawable.ic_account_circle_black_36dp));
                   } else {
                       Glide.with(MainActivity.this)
                               .load(friendlyMessage.getPhotoUrl())
                               .into(viewHolder.userImageView);
                   }
               }
           };
           mFirebaseAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
               @Override
               public void onItemRangeInserted(int positionStart, int itemCount) {
                   super.onItemRangeInserted(positionStart, itemCount);
                   int chatMessageCount = mFirebaseAdapter.getItemCount();
                   int lastVisiblePosition =
                           mLinearLayoutManager.findLastCompletelyVisibleItemPosition();
                   if (lastVisiblePosition == -1 ||
                           (positionStart >= (chatMessageCount - 1) &&
                                   lastVisiblePosition == (positionStart - 1))) {
                       mMessageRecyclerView.scrollToPosition(positionStart);
                   }
               }
           });
           mMessageRecyclerView.setLayoutManager(mLinearLayoutManager);
           mMessageRecyclerView.setAdapter(mFirebaseAdapter);
       }
    }


    At this stage, we did not add authorization tools to the application, so just grant the right to unauthorized reading from the database in the Firebase console. To do this, go to the "Rules" tab and change the settings like this:



    Well, it's time to check what came of it all. We launch our application and this is what we see: a record from the database appeared as a previously sent message.



    Now we can check one more thing. Try adding a few more messages to the database. If everything is done correctly, these messages in real time will be displayed in our chat. Unfortunately, we cannot answer them from the application, so we finish having fun and proceed to finalize the application functionality.

    2. Writing to Firebase


    Everything is simple here. Add the Send button handler to MainActivity:
    mSendButton = (Button) findViewById(R.id.sendButton);
    mSendButton.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) {
           ChatMessage friendlyMessage = new
                   ChatMessage(mMsgEditText.getText().toString(),
                   mUsername,
                   mPhotoUrl);
           mSimpleFirechatDatabaseReference.child("messages")
                   .push().setValue(friendlyMessage);
           mMsgEditText.setText("");
       }
    });


    And it's all! You can run the application again and indulge. Write the text, click on the Send button, it is displayed in the chat, and the corresponding record instantly appears in the database, which we can see through the Firebase console. It remains to add the last feature needed - user authorization.

    3. Authorization in Firebase


    First of all, we take away access to the database from unauthorized users. We included it only for tests. We go to the console, in the "Rules" section and set everything as it was:



    After that, go to the Auth tab and select the authorization method - in this case, through Google:



    We will update build.gradle again, adding dependencies on authorization libraries
    compile 'com.google.firebase:firebase-auth:9.0.0'
    compile 'com.google.android.gms:play-services-auth:9.0.0'


    We also need additional Activity to enter the chat, with the appropriate markup ( AuthorizationActivity.class and activiti_auth.xm l). Just do not forget to register it in the manifest. It would also be nice to have a menu so that we have where to add the option to exit our account ( main_menu.xml ). Well, as a finishing touch, we’ll fix MainActivity a bit.

    We connect authorization


    First, you need to inherit the Activity from the GoogleApiClient.OnConnectionFailedListener interface, and implement this interface itself:
    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
       Toast.makeText(this, "Google Play Services error.", Toast.LENGTH_SHORT).show();
    }
    

    Secondly, add entities for working with authorization:
    this interface:
    private GoogleApiClient mGoogleApiClient;
    private FirebaseAuth mFirebaseAuth;
    private FirebaseUser mFirechatUser;


    And their initialization in onCreate:
    mGoogleApiClient = new GoogleApiClient.Builder(this)
           .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
           .addApi(Auth.GOOGLE_SIGN_IN_API)
           .build();
    mFirebaseAuth = FirebaseAuth.getInstance();
    mFirechatUser = mFirebaseAuth.getCurrentUser();
    if (mFirechatUser == null) {
       startActivity(new Intent(this, AuthorizationActivity.class));
       finish();
       return;
    } else {
       mUsername = mFirechatUser.getDisplayName();
       if (mFirechatUser.getPhotoUrl() != null) {
           mPhotoUrl = mFirechatUser.getPhotoUrl().toString();
       }
    }


    Thirdly, add a call from the menu so that the user can log out:
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
       switch (item.getItemId()) {
           case R.id.sign_out_menu:
               mFirebaseAuth.signOut();
               Auth.GoogleSignInApi.signOut(mGoogleApiClient);
               mUsername = DEFAULT_NAME;
               startActivity(new Intent(this, AuthorizationActivity.class));
               return true;
           default:
               return super.onOptionsItemSelected(item);
       }
    }


    Now you can run and test the application with authorization through a Google account.


    Messages should look like this:



    And in the Firebase console, in the Auth tab, you can see the users who were authorized in our application:



    So, we have a simple application, the backend of which is completely made on Firebase. These features were available earlier, now we just got acquainted with the Firebase tools, now let's move on to more interesting things.

    4. Notifications Using Firebase Notifications


    Firebase Notifications allows you to send notifications to user devices directly from the Firebase console. And you can choose - send to all users, to a specific group (integration also works in the new Firebase tool Audiences) or in general to users of specific devices. But to work with these notifications, you need to teach the application to accept them, and for this you need to configure the appropriate service.

    Connect Firebase Notifications


    First, add the appropriate to build.gradle ... (dependency, command, whatever)
    compile 'com.google.firebase:firebase-messaging:9.0.0'


    Next, create the SimpleFirechatMessagingService service that we will use to manage incoming FCM messages (Firebase Clouds Messages). All we have to do is redefine the onMessageReceived (RemoteMessage remoteMessage) method to handle the incoming notification in some way.

    We also need to create the SimpleFirechatInstanceIdService service for managing FCM logic. Usually it is used to notify the application that a new token has been generated, as well as to obtain this token. Just redefine the onTokenRefresh () method.

    All that remains for us is to register the services in the manifest, after that everything should work.


    Testing notifications


    We launch the application, then go to the Firebase console, the Notifications tab, and send the long-awaited message. The result is instantly displayed on the smartphone display or in the emulator:





    You can get acquainted with the results of sending notifications in the appropriate section of the Firebase console



    5. Firebase Remote Config - we change the application on the fly


    Firebase Remote Config allows you to set a number of parameters in applications that you can update remotely from the server. Thus, you can me the capabilities of your application (design, for example, or translation) without updating the entire .apk and without waiting for it to be downloaded to the application store, and then also with the update on the device. As an example, let's try changing the text on the send message button using this feature.



    Add functionality


    First, go to the Remote Config tab and create the button_name variable



    Now you need to configure the application.
    As in previous times, we start again by adding the appropriate dependency to build.gradle
    compile 'com.google.firebase:firebase-messaging:9.0.0'
    

    ... and add the fetchconfig method, in which we will take the configuration from the server and apply it to the button name.
    public void fetchConfig() {
       long cacheExpiration = 3600; // 1 hour in seconds
       // If developer mode is enabled reduce cacheExpiration to 0 so that
       // each fetch goes to the server. This should not be used in release
       // builds.
       if (mFirebaseRemoteConfig.getInfo().getConfigSettings()
               .isDeveloperModeEnabled()) {
           cacheExpiration = 0;
       }
       mFirebaseRemoteConfig.fetch(cacheExpiration)
               .addOnSuccessListener(new OnSuccessListener() {
                   @Override
                   public void onSuccess(Void aVoid) {
                       // Make the fetched config available via
                       // FirebaseRemoteConfig get calls.
                       mFirebaseRemoteConfig.activateFetched();
                       mSendButton.setText(mFirebaseRemoteConfig.getString("button_name"));
                   }
               })
               .addOnFailureListener(new OnFailureListener() {
                   @Override
                   public void onFailure(@NonNull Exception e) {
                       mSendButton.setText(mFirebaseRemoteConfig.getString("button_name"));
                   }
               });
    }


    We will call this methods in onCreate and in the menu item created for this. To do this, add the following lines to main_menu.xml:


    In onCreate:
    mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
    FirebaseRemoteConfigSettings firebaseRemoteConfigSettings =
           new FirebaseRemoteConfigSettings.Builder()
                   .setDeveloperModeEnabled(true)
                   .build();
    Map defaultConfigMap = new HashMap<>();
    defaultConfigMap.put("button_name", "Send");


    ... and in onOptionsItemSelected:
    case R.id.reconfig:
       fetchConfig();
       return true;
    


    Check the result


    The test algorithm is similar to the past. We start the application, look at the text button, go to the Firebase console, change its value to the button_name. Variable, and call reconfig from the menu.

    Just like that, in half an hour, we created a chat with authorization, cloud notifications and remote configuration based on Firebase capabilities.
    PS: you can download the project and dig into it yourself here .
    I think that today is enough practice, time to take stock.

    What we got as a result


    To store messages, we used Realtime Database - services for managing the NoSQL cloud database. They allow you to save and synchronize data with all connected devices in a matter of milliseconds. In the updated version, the data is saved in the memory or on the device’s disk so that the application remains operational when the connection to the network is lost, and, of course, the data is synchronized after the connection is restored.

    For authorization, we used the Authentication service - a service for identifying and managing users. It supports authentication through a pair of E-mail and password, social networks (Facebook, Twitter, GitHub, Google+) and can be integrated with other existing authorization systems

    For testing notifications, we used Notifications - a service that provides a new UI, accessible through the built-in console and based on Firebase Cloud Messaging. It replaced Google Cloud Messaging technology and sends notifications to users without writing a single line of code. Integration with Firebase Analytics allows you to send notifications to pre-configured user groups.

    Its technologies are closely related to the Cloud Messaging project - a service that combines Firebase with the most popular messaging service - Google Cloud Messaging. Cloud Messaging is cross-platform and runs on iOS, Android, and Web applications.

    And finally, for remote configuration, we used Remote Config - a service that allows you to change and update parts of applications without resorting to a complete update of the package and passing relatively long checks in application stores. For security and ease of operation, you need to think in advance what you want to update remotely and set the appropriate parameters in the key-value video pair. Well, then you can make changes to the operation and appearance of the application by replacing the values ​​of these parameters on the server side. For example, you can update the design of the application for the holiday, and then return everything “as it was”. And no lengthy reviews. Like many other Firebase elements, Notifications integrate into Firebase Analytics. That is, you can select groups of different users (automatically generated by dozens and hundreds of parameters) and send updates to them ...

    In conclusion, I want to say that from the service for storing data, Firebase has turned into a universal platform for mobile developers, with a convenient, friendly interface and the most simplified integration into applications (as you saw, you can actually add it to your application with a couple of clicks). I believe that it would be a big mistake not to pay attention to the output of such a useful and interesting tool. Give it a try. He's really very, very cool ...

    Enough for you today. But we will definitely return to this topic and consider the remaining features of the new Firebase: data storage tools, application testing, monetization and advertising services, as well as pleasant convenient buns such as invites or reliable links. And, of course, the coolest thing ever is the most powerful analytics and user grouping, which is tightly integrated into all other features.



    Thank you all for your attention! The post turned out to be very large, we are all people, so if you find a typo or mistake - write to the PM, we will quickly fix it. And leave questions in the comments. :)

    Also popular now: