Archivo del blog

jueves, 14 de junio de 2018

Firebase Android Codelab

Firebase Android Codelab

1. Visión de conjunto


Image: Working Friendly Chat app.

Bienvenido al codelab de Friendly Chat. En este conjunto de códigos, aprenderá a usar la plataforma de Firebase para crear aplicaciones de Android. Implementarás un cliente de chat y supervisarás su rendimiento usando Firebase.

Lo que aprendes a hacer

  • Permitir a los usuarios iniciar sesión
  • Sincronice los datos usando Firebase Realtime Database.
  • Recibe mensajes en segundo plano con Firebase Notifications.
  • Configure una aplicación con Firebase Remote Config.
  • Rastree los flujos de uso de aplicaciones con Google Analytics para Firebase.
  • Permitir a los usuarios enviar invitaciones para instalar con Firebase Invites.
  • Mostrar anuncios con AdMob.
  • Informes bloqueados con Firebase Crash Reporting.
  • Pon a prueba tu aplicación con Firebase Test Lab.

2. Obtenga el código de muestra

Clona el repositorio de GitHub desde la línea de comando:
$ git clone https://github.com/firebase/friendlychat-android
 

3. Importar la aplicación de inicio

Desde Android Studio, seleccione la android-start directorio () desde el código de muestra descarga (File > Open > .../friendlychat-android/android-start).
Ahora debería tener abierto el proyecto android-start en Android Studio.

4. Crear proyecto de consola de Firebase

  1. Go to the Firebase console.
  2. Select Create New Project, and name your project "FriendlyChat."

Conecte su aplicación de Android

  1. Desde la pantalla de resumen de su nuevo proyecto,
    click Add Firebase to your Android app.
  2. Ingrese el nombre del paquete del codelab: com.google.firebase.codelab.friendlychat.
  3. Introducir el SHA1 de su almacén de claves de firma. Si está utilizando el almacén de claves de depuración estándar, use el comando a continuación para encontrar el SHA1 picadillo:
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v -storepass android

Agregue el archivo google-services.json a su aplicación

Después de agregar el nombre del paquete y SHA1 y seleccionar Continuar, su navegador descarga automáticamente un archivo de configuración que contiene todos los metadatos de Firebase necesarios para su aplicación. Copie el archivo google-services.json en el directorio de la aplicación en su proyecto.

Agrega el complemento google-services a tu aplicación

El complemento google-services usa el archivo google-services.json para configurar su aplicación para usar Firebase. La siguiente línea ya debe estar agregada al final del archivo build.gradle en el directorio de la aplicación de su proyecto (verifique para confirmar):
apply plugin: 'com.google.gms.google-services'

Sincronice su proyecto con archivos gradle

Para asegurarse de que todas las dependencias estén disponibles para su aplicación, debe sincronizar su proyecto con archivos gradle en este momento. Seleccionar Sync Project with Gradle Files () desde la barra de herramientas de Android Studio.

5. Ejecuta la aplicación de inicio

Ahora que ha importado el proyecto a Android Studio y configurado el complemento google-services con su archivo JSON, está listo para ejecutar la aplicación por primera vez. Conecte su dispositivo Android y haga clic Run ()en la barra de herramientas de Android Studio.
La aplicación debe iniciarse en su dispositivo. En este punto, debería ver una lista de mensajes vacía, y enviar y recibir mensajes no funcionará. En la siguiente sección, autenticas a los usuarios para que puedan usar Friendly Chat.

6. Habilitar autenticación

Solicitemos que el usuario inicie sesión antes de leer o publicar cualquier mensaje de conversación amistosa.

Reglas de la base de datos de Firebase Realtime

Acceso a tu Firebase Database está configurado por un conjunto de reglas escritas en un lenguaje de configuración JSON.
Vaya a su proyecto en la consola de Firebase y seleccione Database. Seleccionar Get Started in the Realtime Database opción. Cuando se le soliciten las reglas de seguridad, elija comenzar en cualquiera test mode or locked mode, escoger test mode. Una vez que se establecen las reglas predeterminadas, seleccione la pestaña Reglas y actualice la configuración de las reglas con lo siguiente:
{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}
Para obtener más información sobre cómo funciona esto (incluida la documentación sobre la variable "auth"), consulte la Firebase security documentation.

Configurar las API de autenticación

Antes de que su aplicación pueda acceder al Firebase Authentication APIs en nombre de sus usuarios, tendrá que habilitarlo
  1. Navigate to the Firebase console and select your project
  2. Select Authentication
  3. Select the Sign In Method tab
  4. Palanca the Google switch to enabled (blue)
  5. Press Save on the resulting dialog
Si obtiene errores más adelante en este codelab con el mensaje "CONFIGURATION_NOT_FOUND", vuelve a este paso y revisa tu trabajo.

Añadir dependencia Firebase Auth

El SDK firebase-auth permite una fácil administración de usuarios autenticados de su aplicación. Confirma la existencia de esta dependencia en tu app/build.gradle archivo.

app/build.gradle

implementation 'com.google.firebase:firebase-auth:15.0.0'
Agregue las variables de instancia de Auth en MainActivity class:

MainActivity.java (instance variable)

// Firebase instance variables
private FirebaseAuth mFirebaseAuth;
private FirebaseUser mFirebaseUser;

Verificar el usuario actual

Ahora modifiquemos MainActivity.java para enviar al usuario a la pantalla de inicio de sesión cada vez que abre la aplicación y no está autenticado.
Agregue lo siguiente a onCreate método después mUsername ha sido inicializado:
MainActivity.java
// Initialize Firebase Auth
mFirebaseAuth = FirebaseAuth.getInstance();
mFirebaseUser = mFirebaseAuth.getCurrentUser();
if (mFirebaseUser == null) {
    // Not signed in, launch the Sign In activity
    startActivity(new Intent(this, SignInActivity.class));
    finish();
    return;
} else {
    mUsername = mFirebaseUser.getDisplayName();
    if (mFirebaseUser.getPhotoUrl() != null) {
        mPhotoUrl = mFirebaseUser.getPhotoUrl().toString();
    }
}
A continuación, agregue una nueva caja a onOptionsItemSelected () para manejar el botón de cerrar sesión:
MainActivity.java
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.sign_out_menu:
                mFirebaseAuth.signOut();
                Auth.GoogleSignInApi.signOut(mGoogleApiClient);
                mUsername = ANONYMOUS;
                startActivity(new Intent(this, SignInActivity.class));
                finish();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
Ahora tenemos toda la lógica en el lugar para enviar al usuario a la pantalla de inicio de sesión cuando sea necesario. A continuación, debemos implementar la pantalla de inicio de sesión para autenticar correctamente a los usuarios.

Implementar la pantalla de inicio de sesión

Abra el archivo SignInActivity.java. Aquí se usa un botón de inicio de sesión simple para iniciar la autenticación. En este paso, implementará la lógica para Sign-In con Google, y luego usar esa cuenta de Google para autenticarse con Firebase.
Agregue una variable de instancia Auth en SignInActivity class:
SignInActivity.java
// Firebase instance variables
private FirebaseAuth mFirebaseAuth;
Luego, edita el onCreate() método para inicializar Firebase de la misma manera que lo hizo en MainActivity:
SignInActivity.java
// Initialize FirebaseAuth
mFirebaseAuth = FirebaseAuth.getInstance();
A continuación, inicie sesión con Google. Actualizar SignInActivity's onClick método para que se vea así:
SignInActivity.java
@Override
public void onClick(View v) {
   switch (v.getId()) {
       case R.id.sign_in_button:
           signIn();
           break;
   }
}
Agregue el método de inicio de sesión requerido que realmente presenta al usuario con el Google Sign-In UI.
SignInActivity.java
private void signIn() {
    Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
    startActivityForResult(signInIntent, RC_SIGN_IN);
}
A continuación, agregue el método onActivityResult a SignInActivity para gestionar el resultado de inicio de sesión. Si el resultado de la Google Sign-In was successful, uSe la cuenta para autenticarse con Firebase.
SignInActivity.java
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
    if (requestCode == RC_SIGN_IN) {
        GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
        if (result.isSuccess()) {
            // Google Sign-In was successful, authenticate with Firebase
            GoogleSignInAccount account = result.getSignInAccount();
            firebaseAuthWithGoogle(account);
        } else {
            // Google Sign-In failed
            Log.e(TAG, "Google Sign-In failed.");
        }
    }
}
Agregue el requerido firebaseAuthWithGoogle método para autenticarse con la cuenta de Google iniciada:
SignInActivity.java
private void firebaseAuthWithGoogle(GoogleSignInAccount acct) {
    Log.d(TAG, "firebaseAuthWithGooogle:" + acct.getId());
    AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
    mFirebaseAuth.signInWithCredential(credential)
            .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    Log.d(TAG, "signInWithCredential:onComplete:" + task.isSuccessful());

                    // If sign in fails, display a message to the user. If sign in succeeds
                    // the auth state listener will be notified and logic to handle the
                    // signed in user can be handled in the listener.
                    if (!task.isSuccessful()) {
                        Log.w(TAG, "signInWithCredential", task.getException());
                        Toast.makeText(SignInActivity.this, "Authentication failed.",
                                Toast.LENGTH_SHORT).show();
                    } else {
                        startActivity(new Intent(SignInActivity.this, MainActivity.class));
                        finish();
                    }
                }
            });
}
¡Eso es! Ha implementado la autenticación utilizando Google como proveedor de identidad en solo unas pocas llamadas a métodos y sin necesidad de administrar ninguna configuración del lado del servidor.

Pon a prueba tu trabajo

Ejecuta la aplicación en tu dispositivo. Debería ser enviado inmediatamente a la pantalla de inicio de sesión. Toca el botón de inicio de sesión de Google. Luego, debe enviarlo a la pantalla de mensajes si todo funcionó bien.

7. Leer mensajes

Importar mensajes

  1. En su proyecto en la consola de Firebase, seleccione Database en el menú de navegación izquierdo.
  2. En el menú de desbordamiento de la base de datos, seleccione Import JSON.
  3. Busque el archivo initial_messages.json en la raíz del repositorio clonado y selecciónelo.
  4. Click Import.
Después de importar el archivo JSON, su base de datos debería verse así:
root
        messages
                -K2ib4H77rj0LYewF7dP
                        text: "hello"
                        name: "anonymous"
                -K2ib5JHRbbL0NrztUfO
                        text: "how are you"
                        name: "anonymous"
                -K2ib62mjHh34CAUbide
                        text: "i am fine"
                        name: "anonymous"

Añadir Firebase Realtime Database and Firebase Storage dependencias

En el bloque de dependencias del archivo app / build.gradle, se deben incluir las siguientes dependencias. Para este codelab, ya se agregaron por conveniencia; confirme esto mirando en el archivo app / build.gradle:

Dependency in app/build.gradle

implementation 'com.google.firebase:firebase-database:15.0.0'
implementation 'com.google.firebase:firebase-storage:15.0.0'

Sincronizar mensajes

En esta sección agregamos un código que sincroniza los mensajes recién agregados a la interfaz de usuario de la aplicación al:
  • Initializing the Firebase Realtime Database and adding a listener to handle changes made to the data.
  • Updating the RecyclerView adapter so new messages will be shown.
  • Adding the Database instance variables in the MainActivity class:

MainActivity.java

// Firebase instance variables
private DatabaseReference mFirebaseDatabaseReference;
private FirebaseRecyclerAdapter<FriendlyMessage, MessageViewHolder>
        mFirebaseAdapter;
Modify your MainActivity's onCreate method by replacing mProgressBar.setVisibility(ProgressBar.INVISIBLE); with the code defined below. This code initially adds all existing messages and then listens for new child entries under the messages path in your Firebase Realtime Database. It adds a new element to the UI for each message:

MainActivity.java

// New child entries
mFirebaseDatabaseReference = FirebaseDatabase.getInstance().getReference();
SnapshotParser<FriendlyMessage> parser = new SnapshotParser<FriendlyMessage>() {
            @Override
            public FriendlyMessage parseSnapshot(DataSnapshot dataSnapshot) {
                FriendlyMessage friendlyMessage = dataSnapshot.getValue(FriendlyMessage.class);
                if (friendlyMessage != null) {
                    friendlyMessage.setId(dataSnapshot.getKey());
                }
                return friendlyMessage;
            }
        };

DatabaseReference messagesRef = mFirebaseDatabaseReference.child(MESSAGES_CHILD);
        FirebaseRecyclerOptions<FriendlyMessage> options =
                new FirebaseRecyclerOptions.Builder<FriendlyMessage>()
                        .setQuery(messagesRef, parser)
                        .build();
mFirebaseAdapter = new FirebaseRecyclerAdapter<FriendlyMessage, MessageViewHolder>(options) {
            @Override
            public MessageViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
                LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
                return new MessageViewHolder(inflater.inflate(R.layout.item_message, viewGroup, false));
            }
            
            @Override
            protected void onBindViewHolder(final MessageViewHolder viewHolder,
                                            int position,
                                            FriendlyMessage friendlyMessage) {
       mProgressBar.setVisibility(ProgressBar.INVISIBLE);
if (friendlyMessage.getText() != null) {
   viewHolder.messageTextView.setText(friendlyMessage.getText());
   viewHolder.messageTextView.setVisibility(TextView.VISIBLE);
   viewHolder.messageImageView.setVisibility(ImageView.GONE);
} else if (friendlyMessage.getImageUrl() != null) {
   String imageUrl = friendlyMessage.getImageUrl();
   if (imageUrl.startsWith("gs://")) {
       StorageReference storageReference = FirebaseStorage.getInstance()
               .getReferenceFromUrl(imageUrl);
       storageReference.getDownloadUrl().addOnCompleteListener(
               new OnCompleteListener<Uri>() {
           @Override
           public void onComplete(@NonNull Task<Uri> task) {
               if (task.isSuccessful()) {
                   String downloadUrl = task.getResult().toString();
                   Glide.with(viewHolder.messageImageView.getContext())
                           .load(downloadUrl)
                           .into(viewHolder.messageImageView);
               } else {
                   Log.w(TAG, "Getting download url was not successful.",
                           task.getException());
               }
           }
       });
   } else {
       Glide.with(viewHolder.messageImageView.getContext())
               .load(friendlyMessage.getImageUrl())
               .into(viewHolder.messageImageView);
   }
   viewHolder.messageImageView.setVisibility(ImageView.VISIBLE);
   viewHolder.messageTextView.setVisibility(TextView.GONE);
}


viewHolder.messengerTextView.setText(friendlyMessage.getName());
if (friendlyMessage.getPhotoUrl() == null) {
   viewHolder.messengerImageView.setImageDrawable(ContextCompat.getDrawable(MainActivity.this,
           R.drawable.ic_account_circle_black_36dp));
} else {
   Glide.with(MainActivity.this)
           .load(friendlyMessage.getPhotoUrl())
           .into(viewHolder.messengerImageView);
}

   }
};

mFirebaseAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
   @Override
   public void onItemRangeInserted(int positionStart, int itemCount) {
       super.onItemRangeInserted(positionStart, itemCount);
       int friendlyMessageCount = mFirebaseAdapter.getItemCount();
       int lastVisiblePosition =
              mLinearLayoutManager.findLastCompletelyVisibleItemPosition();
       // If the recycler view is initially being loaded or the 
       // user is at the bottom of the list, scroll to the bottom 
       // of the list to show the newly added message.
       if (lastVisiblePosition == -1 ||
               (positionStart >= (friendlyMessageCount - 1) &&
                       lastVisiblePosition == (positionStart - 1))) {
           mMessageRecyclerView.scrollToPosition(positionStart);
       }
   }
});

mMessageRecyclerView.setAdapter(mFirebaseAdapter);
Appropriately start and stop listening for updates from Firebase Realtime Database. Update the onPause and onResume methods in MainActivity as shown below.

MainActivity.java

@Override
public void onPause() {
    mFirebaseAdapter.stopListening();
    super.onPause();
}

@Override
public void onResume() {
    super.onResume();
    mFirebaseAdapter.startListening();
}

Test message sync

  1. Click Run ().
  2. Add new messages directly in the Database section of the Firebase console. Confirm that they show up in the Friendly-Chat UI.
  • Navigate to the Database section of the Firebase console. From the Data tab, select the '+' sign on the messages element.
  • Give the new element a name of -ABCD (note the '-' sign) and leave the value empty for now.
  • Select the '+' sign on the -ABCD element
  • Give the new element a name of "name" and value of "Mary"
  • Select the '+' sign on the -ABCD element again
  • Give the new element a name of "text" and value of "hello"
  • Select Add
Congratulations, you just added a realtime database to your app!


8. Send Messages

Implement text message sending

In this section, you will add the ability for app users to send text messages. The code snippet below listens for click events on the send button, creates a new FriendlyMessage object with the contents of the message field, and pushes the message to the database. The push() method adds an automatically generated ID to the pushed object's path. These IDs are sequential which ensures that the new messages will be added to the end of the list.
Update the onClick method of mSendButton in the onCreate method in the MainActivity class. This code is at the bottom of the onCreate method already. Update the onClick body to match the code below:

MainActivity.java

mSendButton = (Button) findViewById(R.id.sendButton);
mSendButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
       FriendlyMessage friendlyMessage = new 
               FriendlyMessage(mMessageEditText.getText().toString(),
                               mUsername,
                               mPhotoUrl,
                               null /* no image */);
       mFirebaseDatabaseReference.child(MESSAGES_CHILD)
               .push().setValue(friendlyMessage);
       mMessageEditText.setText("");
   }
});

Implement image message sending

In this section, you will add the ability for app users to send image messages. Creating an image message is done with these steps:
  • Select image
  • Handle image selection
  • Write temporary image message to the RTDB
  • Begin to upload selected image
  • Update image message URL to that of the uploaded image, once upload is complete

Select Image

With the following code snippet you will allow the user to select an image from the device's local storage. Update the onClick method of mAddMessageImageView in the onCreate method in the MainActivity class. This code is at the bottom of the onCreate method already. Update the onClick body to match the code below:

MainActivity.java

mAddMessageImageView = (ImageView) findViewById(R.id.addMessageImageView);
mAddMessageImageView.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
       Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
       intent.addCategory(Intent.CATEGORY_OPENABLE);
       intent.setType("image/*");
       startActivityForResult(intent, REQUEST_IMAGE);
   }
});

Handle image selection and write temp message

Once the user has selected an image, a call to the MainActivity's onActivityResult will be fired. This is where you handle the user's image selection. Using the code snippet below, add the onActivityResult method to MainActivity. In this function you will write a message with a temporary image url to the database indicating the image is being uploaded.

MainActivity.java

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   super.onActivityResult(requestCode, resultCode, data);
   Log.d(TAG, "onActivityResult: requestCode=" + requestCode + ", resultCode=" + resultCode);

   if (requestCode == REQUEST_IMAGE) {
       if (resultCode == RESULT_OK) {
           if (data != null) {
               final Uri uri = data.getData();
               Log.d(TAG, "Uri: " + uri.toString());

               FriendlyMessage tempMessage = new FriendlyMessage(null, mUsername, mPhotoUrl,
                       LOADING_IMAGE_URL);
               mFirebaseDatabaseReference.child(MESSAGES_CHILD).push()
                       .setValue(tempMessage, new DatabaseReference.CompletionListener() {
                           @Override
                           public void onComplete(DatabaseError databaseError,
                                                  DatabaseReference databaseReference) {
                               if (databaseError == null) {
                                   String key = databaseReference.getKey();
                                   StorageReference storageReference =
                                           FirebaseStorage.getInstance()
                                           .getReference(mFirebaseUser.getUid())
                                           .child(key)
                                           .child(uri.getLastPathSegment());

                                   putImageInStorage(storageReference, uri, key);
                               } else {
                                   Log.w(TAG, "Unable to write message to database.",
                                           databaseError.toException());
                               }
                           }
                       });
           }
       }
   }
}

Upload image and update message

Add the method putImageInStorage to MainActivity. It is called in onActivityResult to initiate the upload of the selected image. Once the upload is complete you will update the message to use the appropriate image.

MainActivity.java

private void putImageInStorage(StorageReference storageReference, Uri uri, final String key) {
   storageReference.putFile(uri).addOnCompleteListener(MainActivity.this,
           new OnCompleteListener<UploadTask.TaskSnapshot>() {
               @Override
               public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {
                   if (task.isSuccessful()) {
                       FriendlyMessage friendlyMessage =
                               new FriendlyMessage(null, mUsername, mPhotoUrl,
                                       task.getResult().getMetadata().getDownloadUrl()
                                               .toString());
                       mFirebaseDatabaseReference.child(MESSAGES_CHILD).child(key)
                               .setValue(friendlyMessage);
                   } else {
                       Log.w(TAG, "Image upload task was not successful.",
                               task.getException());
                   }
               }
           });
}

Test Sending Messages

  1. Click the Run button.
  2. Enter a message and hit the send button, the new message should be visible in the app UI and in the Firebase console.
  3. Tap the "+" image to select an image from your device. The new message should be visible first with a placeholder image, and then with the selected image once the image upload is complete. The new message should also be visible in the Firebase console, as an object in the Database and as a blob in Storage.