Image: Working Friendly Chat app.

Добро пожаловать в лабораторную работу Friendly Chat. В ней вы узнаете, как использовать платформу Firebase для создания Android-приложений. Вы сделаете клиент чата и сможете отследить его работу с помощью Firebase.

Что будет сделано

Что вам понадобится

После того, как вы соберете все необходимое, настройте среду.

Клонируйте репозиторий GitHub с помощью командной строки:

$ git clone https://github.com/firebase/friendlychat

В Android Studio откройте директорию android-start () в скачанном тестовом коде (File > Open > .../firebase-codelabs/android-start).

Так у вас должен появиться открытый проект android-start в Android Studio.

  1. Перейдите в консоль Firebase.
  2. Выберите Create New Project и назовите ваш проект "FriendlyChat."

Подключаем ваше Android приложение

  1. С начального экрана вашего нового проекта кликните Add Firebase to your Android app.
  2. Введите имя пакета: com.google.firebase.codelab.friendlychat.
  3. Введите SHA1 вашего хранилища с ключами. Если вы используете стандартное отладочное хранилище, используйте такую команду, чтобы найти хэш SHA1:
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v -storepass android

Добавление файла google-services.json в ваше приложение

После ввода имени пакета, SHA1 и выбора Continue, ваш браузер автоматически скачает конфигурационный файл, который содержит все необходимые для вашего приложения метаданные Firebase. Скопируйте файл google-services.json в директорию app вашего проекта.

Добавляем плагин google-services в ваше приложение

Плагин google-services использует файл google-services.json для конфигурирования работы вашего приложения с Firebase. Следующая строчка должна уже быть добавлена в файл build.gradle, который находится в директории app вашего проекта (проверьте это на всякий случай):

apply plugin: 'com.google.gms.google-services'

Синхронизация проекта с файлами gradle

Чтобы быть уверенными, что все зависимости доступны вашему приложению, вы должны сейчас синхронизировать проект с файлами gradle. Выберите Sync Project with Gradle Files () из меню Android Studio.

Теперь, когда вы импортировали проект в Android Studio и настроили плагин google-services с вашим JSON файлом, мы можем первый раз запустить тестовое приложение. Подключите ваше Android устройство и кликните Run () в меню Android Studio.

Приложение должно запуститься на вашем устройстве. Пока вы можете видеть только пустой список сообщений, а получение и отправка не работают. В следующем разделе мы аутентифицируем пользователей, чтобы они могли принять участие в Дружеском Чате.

Давайте потребуем аутентификации от пользователей перед чтением или отправкой сообщений в Friendly Chat.

Правила Firebase Realtime Database

Доступ к вашей Firebase Database настраивается набором правил, записанных на языке JSON.

Перейдите в ваш проект в консоли Firebase, выберите Database и закладку Rules. Правила должны выглядеть примерно так:

{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

Если вы хотите узнать больше о том, как это работает (включая сведения о переменной "auth"), посмотрите документацию по безопасности Firebase.

Настраиваем Authentication API

Перед тем, как ваше приложение сможет получить доступ к Firebase Authentication API по поручению ваших пользователей, вам надо включить их

  1. Перейдите в консоль Firebase и выберите ваш проект
  2. Выберите Auth
  3. Выберите закладку Sign In Method
  4. Включите Google (он станет голубым)
  5. Нажмите Save в появившемся диалоге

Если вы позднее получите сообщение "CONFIGURATION_NOT_FOUND", вернитесь к этому пункту и проверьте еще раз все сделанное.

Добавляем зависимости Firebase Auth

Firebase-auth позволяет вам просто управлять аутентификацией пользователей вашего приложения. Проверьте наличие зависимости в вашем файле app/build.gradle.

app/build.gradle

compile 'com.google.firebase:firebase-auth:9.2.1'

Добавьте Auth переменные в ваш класс MainActivity:

MainActivity.java (instance variable)

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

Проверка текущего пользователя

Измените MainActivity.java для того, чтобы пользователь отправлялся на экран входа, когда он открывает приложение и не зарегистрирован.

Добавьте такой метод onCreate после инициализации mUsername:

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();
    }
}

Потом добавьте новый пункт в onOptionsItemSelected() для обработки кнопки выхода:

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));
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

Теперь у нас готова вся логика для отправки пользователя на экран входа, когда это необходимо. Дальше нам надо сделать сам экран входа для аутентификации пользователей.

Делаем Sign-In экран

Откройте файл SignInActivity.java. Тут есть кнопка входа, которая инициирует аутентификацию. Сейчас вы сделаете логику Входа с Google, а потом используете этот аккаунт Google для аутентификации в Firebase.

Добавьте Auth переменные в класс SignInActivity:

SignInActivity.java

// Firebase instance variables
private FirebaseAuth mFirebaseAuth;

Потом отредактируйте метод onCreate() для инициализации Firebase так же, как вы это сделали в MainActivity:

SignInActivity.java

// Initialize FirebaseAuth
mFirebaseAuth = FirebaseAuth.getInstance();

Потом инициируйте вход с Google. Обновите SignInActivity в методе onClick:

SignInActivity.java

@Override
public void onClick(View v) {
   switch (v.getId()) {
       case R.id.sign_in_button:
           signIn();
           break;
   }
}

Добавьте требуемый метод signIn, который на самом деле представляет пользователя с помощью Google Sign In UI.

SignInActivity.java

private void signIn() {
        Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
        startActivityForResult(signInIntent, RC_SIGN_IN);
    }

Далее, обработайте результаты Google Sign In, добавьте метод onActivityResult к SignInActivity для обработки результатов входа. Если вход в Google прошел успешно, то используйте аккаунт для аутентификации в 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.");
            }
        }
    }

Добавьте метод firebaseAuthWithGoogle для аутентификации с полученным аккаунтом Google:

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();
                        }
                    }
                });
    }

Вот и все! Вы реализовали аутентификацию, используя Google в качестве Identity Provider-а, всего в нескольких методах и без необходимости управлять своей серверной конфигурацией.

Тестируем работу

Запустите приложение на устройстве. Вы должны быть немедленно направлены на экран входа. Нажмите кнопку Google Sign In. Если все работает правильно, то вы должны попасть на экран с сообщениями.

Импорт сообщений

  1. В вашем проекте в консоли Firebase выберите Database слева в навигационной панели.
  2. В выпадающем меню Database выберите Import JSON.
  3. Найдите файл initial_messages.json в корне клонированного репозитория и выберите его.
  4. Нажмите Import.

После импорта JSON файла ваша база данных должна выглядеть так:

root
        messages
                -K2ib4H77rj0LYewF7dP
                        text: "hello"
                        name: "anonymous"
                -K2ib5JHRbbL0NrztUfO
                        text: "how are you"
                        name: "anonymous"
                -K2ib62mjHh34CAUbide
                        text: "i am fine"
                        name: "anonymous"

Добавляем зависимости Firebase Realtime Database

В разделе зависимостей файла app/build.gradle надо включить и базу данных. В этой лабораторной работе мы уже добавили ее для удобства, просто проверьте наличие строки в app/build.gradle:

Зависимости в app/build.gradle

compile 'com.google.firebase:firebase-database:9.2.1'

Синхронизация сообщений

Добавим код, который синхронизирует вновь добавленные сообщения с интерфейсом приложения.

Инициализируем Firebase Realtime Database и добавим слушателя для обработки изменений в данных. Обновим адаптер RecyclerView для показа новых сообщений.

Добавьте переменные в класс MainActivity:

MainActivity.java

// Firebase instance variables
private DatabaseReference mFirebaseDatabaseReference;
private FirebaseRecyclerAdapter<FriendlyMessage, MessageViewHolder>
        mFirebaseAdapter;

Измените метод onCreate в MainActivity, заменив mProgressBar.setVisibility(ProgressBar.INVISIBLE); на код, который написан ниже. Он изначально добавляет все существующие сообщения и затем ожидает появления новых записей в Firebase Realtime Database. Он добавляет новый элемент в UI для каждого сообщения:

MainActivity.java

// New child entries
mFirebaseDatabaseReference = FirebaseDatabase.getInstance().getReference();
mFirebaseAdapter = new FirebaseRecyclerAdapter<FriendlyMessage, 
                                               MessageViewHolder>(
               FriendlyMessage.class,
               R.layout.item_message,
               MessageViewHolder.class,
               mFirebaseDatabaseReference.child(MESSAGES_CHILD)) {

   @Override
   protected void populateViewHolder(MessageViewHolder viewHolder,
           FriendlyMessage friendlyMessage, int position) {
       mProgressBar.setVisibility(ProgressBar.INVISIBLE);
       viewHolder.messageTextView.setText(friendlyMessage.getText());
       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.setLayoutManager(mLinearLayoutManager);
mMessageRecyclerView.setAdapter(mFirebaseAdapter);

Тестируем синхронизацию сообщений

  1. Кликните Run ().
  2. Добавьте новое сообщение прямо в базу данных в консоли Firebase. Убедитесь, что оно показалось в интерфейсе Friendly-Chat.

Поздравляем, вы только что подключили ваше приложение к базе данных!

Внедряем отправку сообщений

В этом разделе вы дадите пользователям приложения возможность отправлять сообщения. Кусок кода ниже слушает событие клика на кнопке отправки, создает новый объект FriendlyMessage с содержанием сообщения и пушит его в базу данных. Метод push() добавляет автоматически сгенерированный ID к пути добавляемого объекта. Этот ID последователен, а значит, новые сообщения будут добавляться в конец списка.

Обновите метод onClick в mSendButton в методе onCreate в классе MainActivity. Этот код уже находится в конце метода onCreate, обновите onClick, чтобы он соответствовал ниже приведенному:

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);
       mFirebaseDatabaseReference.child(MESSAGES_CHILD)
               .push().setValue(friendlyMessage);
       mMessageEditText.setText("");
   }
});

Тестируем отправку сообщений

  1. Кликните Run.
  2. Введите сообщение и нажмите кнопку отправки, новое сообщение должно появиться в приложении и в консоли Firebase.

Вы можете использовать Firebase Cloud Messaging (FCM) для отправки уведомлений пользователям вашего приложения. В этой главе вы настроите приложение на получение уведомлений, которые вы можете отправлять из консоли Firebase.

Добавляем зависимости FCM

Зависимости firebase-messaging дают возможность отправлять и получать FCM сообщения. Проверьте существование зависимости в вашем файле app/build.gradle.

app/build.gradle

compile 'com.google.firebase:firebase-messaging:9.2.1'

Добавление FCM Сервисов

Класс RegistrationIntentService - это фоновый сервис, используемый для запроса токена InstanceID, идентифицирующего приложение для FCM сервера. Он также подписывается на тему, которая будет использоваться для отправки уведомлений (при помощи тематических сообщений (topic messaging)).

Класс MyFirebaseMessagingService будет фоновым сервисом, который обрабатывает входящие FCM сообщения.

Обновите его расширением FirebaseMessagingService, который реализован в библиотеке firebase-fcm, которую мы добавили ранее. Он автоматически обрабатывает уведомительные сообщения - сообщения, которые отправляет сервер, и которые показываются пользователю в уведомлении. Чтобы обрабатывать сообщения с данными (они незаметно передаются приложению, в отличие от уведомительных, которые показывают уведомления), вы можете переопределить метод onMessageReceived из класса FirebaseMessagingService:

MyFirebaseMessagingService.java

public class MyFirebaseMessagingService extends FirebaseMessagingService {

   private static final String TAG = "MyFMService";

   @Override
   public void onMessageReceived(RemoteMessage remoteMessage) {
       // Handle data payload of FCM messages.
       Log.d(TAG, "FCM Message Id: " + remoteMessage.getMessageId());
       Log.d(TAG, "FCM Notification Message: " +
               remoteMessage.getNotification());
       Log.d(TAG, "FCM Data Message: " + remoteMessage.getData());
   }
}

Класс MyFirebaseInstanceIdService будет сервисом для обработки логики FCM. Этот сервис используется для уведомления приложения при генерации нового токена InstanceID и его получении.

Измените его, расширив FirebaseInstanceIdService и переписав метод onTokenRefresh для подписки на тему. Используйте следующий код для обновления метода onTokenRefresh в MyFirebaseInstanceIdService:

MyInstanceIDListenerService.java

public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {

   private static final String TAG = "MyFirebaseIIDService";
   private static final String FRIENDLY_ENGAGE_TOPIC = "friendly_engage";
  
   /**
    * The Application's current Instance ID token is no longer valid 
    * and thus a new one must be requested.
    */
   @Override
   public void onTokenRefresh() {
       // If you need to handle the generation of a token, initially or
       // after a refresh this is where you should do that.
       String token = FirebaseInstanceId.getInstance().getToken();
       Log.d(TAG, "FCM Token: " + token);

       // Once a token is generated, we subscribe to topic.
       FirebaseMessaging.getInstance()
               .subscribeToTopic(FRIENDLY_ENGAGE_TOPIC);
   }
}

Добавьте декларацию сервиса в MyFirebaseListenerService и MyInstanceIdListenerService. Добавьте такое объявление дочерним для элемента приложения:

AndroidManifest.xml

<service
   android:name=".MyFirebaseMessagingService"
   android:exported="false">
   <intent-filter>
       <action android:name="com.google.firebase.MESSAGING_EVENT" />
   </intent-filter>
</service>

<service
   android:name=".MyFirebaseInstanceIdService"
   android:exported="false">
   <intent-filter>
       <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
   </intent-filter>
</service>

Вот и все! FCM готово получать сообщения.

Тестируем фоновые уведомления

  1. Запустите обновленное приложение.
  2. Нажмите кнопку "Home" устройства (или другим способом отправьте приложение в фон).
  3. Используйте Composer в консоли Firebase для отправки уведомления.
  1. В консоли Firebase выберите Notifications в левой навигационной панели.
  2. Выберите Send Your First Message.
  3. Напишите в Message Text "Friendly Chat?".
  4. Выберите приложение, которое мы ранее подключили в качестве целевого.
  5. Кликните Send Message
  1. Убедитесь, что сообщение получено и уведомление показывается на экране устройства. При тапе на нем пользователь должен вернуться в приложение.

Ура! Теперь вы можете просто вовлекать ваших пользователей в работу приложения при помощи FCM. Посмотрите документацию, чтобы узнать больше о FCM.

Firebase Remote Config позволяет вам удаленно конфигурировать приложение без необходимости изменять код. В этой работе "Дружеские сообщения" ограничены по длине. Если мы зададим максимальную длину через Firebase Remote Config, а не жестко пропишем ее в коде, то обновлять этот параметр мы сможем через консоль Firebase.

Добавляем правило в консоли Firebase

В разделе Remote Config консоли Firebase кликните Add Parameter. Установите ключ параметра в friendly_msg_length, а сам параметр равный 10. Не забудьте кликнуть Publish, когда закончите.

Добавляем зависимости Firebase Remote Config

Зависимость firebase-config дает возможность удаленно конфигурировать приложение. Проверьте, что она добавлена в ваш файл app/build.gradle:

app/build.gradle

compile 'com.google.firebase:firebase-config:9.2.1'

Добавьте переменные Firebase Remote Config в класс MainActivity:

MainActivity.java (instance variable)

// Firebase instance variables
private FirebaseRemoteConfig mFirebaseRemoteConfig;

Запрос и использование конфига

В методе MainActivity onCreate добавьте такой код для инициализации FirebaseRemoteConfig и последующего вовлечения метода fetchConfig. Добавьте его прямо над инициализацией Firebase Realtime Database:

MainActivity.java

// Initialize Firebase Remote Config.
mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();

// Define Firebase Remote Config Settings.
FirebaseRemoteConfigSettings firebaseRemoteConfigSettings =
       new FirebaseRemoteConfigSettings.Builder()
       .setDeveloperModeEnabled(true)
       .build();

// Define default config values. Defaults are used when fetched config values are not
// available. Eg: if an error occurred fetching values from the server.
Map<String, Object> defaultConfigMap = new HashMap<>();
defaultConfigMap.put("friendly_msg_length", 10L);

// Apply config settings and default values.
mFirebaseRemoteConfig.setConfigSettings(firebaseRemoteConfigSettings);
mFirebaseRemoteConfig.setDefaults(defaultConfigMap);

// Fetch remote config.
fetchConfig();

Добавьте методы fetchConfig и applyRetrievedLengthLimit в MainActivity, они извлекают и активируют полученную из Remote Config API конфигурацию. Она определяет максимальное количество символов, которое может содержать Friendly Message. По умолчанию это 10, это установлено в объекте FirebaseRemoteConfigSettings.

MainActivity.java

// Fetch the config to determine the allowed length of messages.
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<Void>() {
               @Override
               public void onSuccess(Void aVoid) {
                   // Make the fetched config available via
                   // FirebaseRemoteConfig get<type> calls.
                   mFirebaseRemoteConfig.activateFetched();
                   applyRetrievedLengthLimit();
               }
           })
           .addOnFailureListener(new OnFailureListener() {
               @Override
               public void onFailure(@NonNull Exception e) {
                   // There has been an error fetching the config
                   Log.w(TAG, "Error fetching config: " + 
                           e.getMessage());
                   applyRetrievedLengthLimit();
               }
           });
}


/**
 * Apply retrieved length limit to edit text field. 
 * This result may be fresh from the server or it may be from cached
 * values.
 */
private void applyRetrievedLengthLimit() {
   Long friendly_msg_length =
           mFirebaseRemoteConfig.getLong("friendly_msg_length");
   mMessageEditText.setFilters(new InputFilter[]{new 
           InputFilter.LengthFilter(friendly_msg_length.intValue())});
   Log.d(TAG, "FML is: " + friendly_msg_length);
}

Добавьте вызов fetchConfig в метод onOptionsItemSelected в MainActivity. Теперь метод onOptionsItemSelected должен выглядеть так:

MainActivity.java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
   switch (item.getItemId()) {
       case R.id.fresh_config_menu:
           fetchConfig();
           return true;
       case R.id.sign_out_menu:
           mFirebaseAuth.signOut();
           mUsername = ANONYMOUS;
           startActivity(new Intent(this, SignInActivity.class));
           return true;
       default:
           return super.onOptionsItemSelected(item);
   }
}

Тестируем удаленную конфигурацию

  1. Кликните Run.
  2. Проверьте, что длина сообщений ограничена 10 символами. Обновите значение в Remote Config с 10 на 15 в консоли Firebase и опубликуйте это изменение. Из выпадающего меню приложений выберите Fresh Config и подтвердите, что длина Friendly Message теперь ограничена 15 символами.

Поздравляем! Теперь вы знаете, как обновлять ваше приложение не перевыпуская его.

Firebase App Invites - простой способ для ваших пользователей поделиться приложением с друзьями через Email или SMS.

Добавляем зависимости AppInvite

Проверьте наличие зависимости firebase-appinvites в файле app/build.gradle:

app/build.gradle

compile 'com.google.android.gms:play-services-appinvite:9.2.1'

Настраиваем GoogleApiClient

Сделайте в MainActivity реализацию интерфейса GoogleApiClient.OnConnectionFailedListener. Это уже, по идее, сделано, так что просто проверьте:

MainActivity.java

public class MainActivity extends AppCompatActivity implements
       GoogleApiClient.OnConnectionFailedListener {

Реализуйте требуемый метод onConnectionFailed. Он также уже должен быть, проверьте:

MainActivity.java

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
   Log.d(TAG, "onConnectionFailed:" + connectionResult);
}

Добавьте переменную GoogleApiClient в MainActivity:

MainActivity.java

private GoogleApiClient mGoogleApiClient;

Инициализируется mGoogleApiClient в методе onCreate в MainActivity. AppInvites инициализируется обращением к startActivityForResult, это позволяет AppInvites UI обрабатывать генерацию приглашений и затем возвращать статус завершения в вызывающую активити при помощи метода onActivityResult. Обновите инициализацию GoogleApiClient для метода onCreate в MainActivity при помощи этого:

MainActivity.java

mGoogleApiClient = new GoogleApiClient.Builder(this)
       .enableAutoManage(this, this)
       .addApi(Auth.GOOGLE_SIGN_IN_API)
       .addApi(AppInvite.API)
       .build();

Отправка приглашений

Добавьте метод sendInvitation к MainActivity так, чтобы он создавал и запускал Намерение, которое дает пользователю возможность отправлять приглашения.

MainActivity.java

private void sendInvitation() {
   Intent intent = new AppInviteInvitation.IntentBuilder(getString(R.string.invitation_title))
           .setMessage(getString(R.string.invitation_message))
           .setCallToActionText(getString(R.string.invitation_cta))
           .build();
   startActivityForResult(intent, REQUEST_INVITE);
}

Обработайте результат обращения к активити приглашения, чтобы показать успешность его отправки или сбой.

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_INVITE) {
       if (resultCode == RESULT_OK) {
           // Check how many invitations were sent.
           String[] ids = AppInviteInvitation
                   .getInvitationIds(resultCode, data);
           Log.d(TAG, "Invitations sent: " + ids.length);
       } else {
           // Sending failed or it was canceled, show failure message to
           // the user
           Log.d(TAG, "Failed to send invitation.");
       }
   }
}

Добавьте вызов sendInvitation к методу onOptionsItemSelected в MainActivity. Метод onOptionsItemSelected теперь должен выглядеть так:

MainActivity.java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
   switch (item.getItemId()) {
       case R.id.invite_menu:
           sendInvitation();
           return true;
       case R.id.fresh_config_menu:
           fetchConfig();
           return true;
       case R.id.sign_out_menu:
           mFirebaseAuth.signOut();
           mUsername = ANONYMOUS;
           startActivity(new Intent(this, SignInActivity.class));
           return true;
       default:
           return super.onOptionsItemSelected(item);
   }
}

Тестируем приглашения в приложение

  1. Кликните Run.
  2. Тапните на выпадающее меню справа сверху.
  3. Выберите Invite.
  4. Вы должны увидеть интерфейс приглашения, который позволяет вам выбрать Email и SMS контактов и отправить собственное приглашение. Вам нужен контроль над аккаунтом получателя для того, чтобы проверить отправку приглашения.
  5. Нажмите на отправку и удостоверьтесь, что приглашения отосланы выбранным контактам.
  6. Проверьте, что выбранный контакт попадает на экран установки приложения из приглашения.

Теперь вы знаете как включить приглашения. Поздравляем!

Firebase Analytics дает возможность отслеживать то, как пользователи перемещаются по вашему приложению, где у них все получается, а где они застревают. Она также поможет вам понять, какие функции приложения используются больше всего.

Добавляем зависимости Analytic

Проверьте наличие firebase-analytics в вашем app/build.gradle:

app/build.gradle

compile 'com.google.firebase:firebase-analytics:9.2.1'

Инициализируем аналитику

Добавьте переменную FirebaseAnalytics в MainActivity:

MainActivity.java

private FirebaseAnalytics mFirebaseAnalytics;

В MainActivity инициализируйте mFirebaseAnalytics добавив следующую строку в метод onCreate. Инициализировав Firebase Analytics вы автоматически будете отслеживать жизненный цикл вашего приложения в каждой пользовательской сессии без написания какого-либо дополнительного кода.

MainActivity.java

mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);

Отправка кастомный событий

Работающая Firebase Analytics предоставляет вам типовые метрики, такие как установки и сессии, однако вы, возможно, захотите добавить свои собственные события, которые помогут вам понять, как пользователи взаимодействуют с вашим приложением. Для отправки кастомного события обратитесь к mFirebaseAnalytics.logEvent() с информацией о нем.

В MainActivity запишем событие приглашения в вызове onActivityResult. Ваш вызов onActivityResult должен стать таким, как показано ниже. Вы можете видеть, что в любом случае мы отправляем событие SHARE, но с разными параметрами - удачного завершения или нет.

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_INVITE) {
       if (resultCode == RESULT_OK) {
           Bundle payload = new Bundle();
           payload.putString(FirebaseAnalytics.Param.VALUE, "sent");
           mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.SHARE,
                   payload);
           // Check how many invitations were sent and log.
           String[] ids = AppInviteInvitation.getInvitationIds(resultCode,
                   data);
           Log.d(TAG, "Invitations sent: " + ids.length);
       } else {
           Bundle payload = new Bundle();
           payload.putString(FirebaseAnalytics.Param.VALUE, "not sent");
           mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.SHARE,
                  payload);
           // Sending failed or it was canceled, show failure message to
           // the user
           Log.d(TAG, "Failed to send invitation.");
       }
   }
}

Любое событие, которое вы добавляете в Firebase Analytics, будет агрегировано, анонимизировано и отправлено в консоль Firebase в течение 24 часов.

AdMob - простой способ монетизировать ваше приложение, вы просто добавляете позицию AdView, а Google доставляет вам рекламу.

Добавляем зависимости AdMob

Проверьте наличие зависимостей play-services-ads в файле app/build.gradle:

app/build.gradle

compile 'com.google.android.gms:play-services-ads:9.2.1'

Добавляем пространство имен рекламы

Включите пространство имен в корень тега RelativeLayout в activity_main.xml file.

activity_main.xml

<RelativeLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   xmlns:ads="http://schemas.android.com/apk/res-auto"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:paddingBottom="@dimen/activity_vertical_margin"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   tools:context="com.google.firebase.codelab.friendlychat.MainActivity">

Добавляем AdView в главный макет

Добавим view с рекламой. В вашем файле activity_main.xml в корне RelativeLayout добавьте следующие теги AdView.

activity_main.xml

<com.google.android.gms.ads.AdView
   android:id="@+id/adView"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_centerHorizontal="true"
   android:layout_alignParentTop="true"
   ads:adSize="BANNER"
   ads:adUnitId="@string/banner_ad_unit_id">
</com.google.android.gms.ads.AdView>

Обновите элемент RecyclerView, добавив AdView, RecyclerView теперь должен выглядеть так:

activity_main.xml

<android.support.v7.widget.RecyclerView
   android:id="@+id/messageRecyclerView"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:layout_below="@+id/adView"
   android:layout_above="@+id/linearLayout"/>

Добавляем переменные AdView

В MainActivity добавьте переменные, представляющие AdView:

MainActivity.java

private AdView mAdView;

Запрос рекламы

В MainActivity в методе onCreate запросите рекламу для размещения в AdView:

MainActivity.java

mAdView = (AdView) findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder().build();
mAdView.loadAd(adRequest);

Обработка событий жизненного цикла

В MainActivity добавьте обработку событий жизненного цикла Activity: паузу, возобновление и уничтожение.

MainActivity.java

@Override
public void onPause() {
   if (mAdView != null) {
       mAdView.pause();
   }
   super.onPause();
}

/** Called when returning to the activity */
@Override
public void onResume() {
   super.onResume();
   if (mAdView != null) {
       mAdView.resume();
   }
}

/** Called before the activity is destroyed */
@Override
public void onDestroy() {
   if (mAdView != null) {
       mAdView.destroy();
   }
   super.onDestroy();
}

Тестируем AdMob

  1. Кликните Run.
  2. Проверьте показ тестовой рекламы (как на скриншоте выше).

Firebase Crash позволяет вашему приложению сохранять отчеты о сбоях, когда они случаются, и записывать события, которые к ним привели.

Добавляем зависимость Firebase Crash

Проверьте наличие firebase-crash в app/build.gradle:

app/build.gradle

compile 'com.google.firebase:firebase-crash:9.2.1'

Инициализируем сбой

Добавьте обработчик для клика на элементе меню Cause Crash. Обновите метод onOptionsItemSelected в MainActivity, чтобы он стал похож на это:

MainActivity.java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
   switch (item.getItemId()) {
       case R.id.crash_menu:
           FirebaseCrash.logcat(Log.ERROR, TAG, "crash caused");
           causeCrash();
           return true;
       case R.id.invite_menu:
           sendInvitation();
           return true;
       case R.id.fresh_config_menu:
           fetchConfig();
           return true;
       case R.id.sign_out_menu:
           mFirebaseAuth.signOut();
           mUsername = ANONYMOUS;
           startActivity(new Intent(this, SignInActivity.class));
           return true;
              default:
           return super.onOptionsItemSelected(item);
   }
}

Добавляем метод causeCrash

В MainActivity добавьте такой метод causeCrash.

MainActivity.java

private void causeCrash() {
   throw new NullPointerException("Fake null pointer exception");
}

Тестируем Firebase Crash

  1. Кликните Run.
  2. Выберите пункт Cause Crash в выпадающем меню.
  3. Проверьте, что Cause Crash вызвал сбой приложения.
  4. В logcat проверьте загрузку отчета о сбое.

Теперь вы знаете, как автоматически уведомлять о сбоях в вашем приложении. Прекрасно!

Firebase Test Lab дает вам возможность тестировать приложение на разного рода Android устройствах с разным уровнем API и разными локалями. Прекрасная сторона такого тестирования заключается в том, что оно происходит автоматически в облаке, а значит вам на надо собирать свою коллекцию устройств.

Апгрейдим ваш проект Firebase до Blaze

Для целей этой лабораторной работы надо, чтобы вы обновили ваш проект Firebase до плана Blaze (Pay as you go). Вы можете пропустить этот шаг, если хотите остаться на бесплатном плане. Посмотрите это руководство для улучшения вашего плана.

Добавление инструментов Espresso для тестирования

Во-первых, нам надо добавить инструментальные тесты, которым будет следовать Firebase Test Lab. В файле MainActivityEspressoTest.java добавьте следующий метод тестирования в класс.

MainActivityEspressoTest.java

@Test
    public void verifySignUpButtonDisplayed() {
        onView(ViewMatchers.withId(R.id.sign_in_button)).check(matches(isDisplayed()));
    }

Создаем новую конфигурацию для теста

Кликните app > Edit Configurations...

В окне Configurations кликните на (+), выберите Android Tests для создания новой тестовой конфигурации.

Настраиваем конфигурацию для запуска тестов в Firebase Test Lab

В окне Configurations задайте новую конфигурацию запуска как показано ниже:

  1. Name: FriendlyChat Test
  2. Module: app
  3. Test: Class
  4. Class: com.google.firebase.codelab.friendlychat.MainActivityEspressoTest
  5. В Deployment Target Options в меню Target выберите Cloud Test Lab Device Matrix. Если вы еще не вошли, кликните Connect to Google Cloud Platform для связи с Firebase Test Lab.
    В Cloud project:, кликните иконку и выберите ваш проект Google Cloud Platform из списка.

Настраиваем тестовую матрицу для выбора тестовых устройств

Android Studio содержит Sample configuration, которая автоматически конфигурирует тестовую матрицу с помощью нескольких широко распространенных устройств и версий платформы. Чтобы использовать такую Тестовую конфигурацию, выберите ее в списке Matrix configuration и перейдите к шагу 2, как описано ниже. Чтобы создать вашу собственную тестовую матрицу и выбрать определенные устройства, версии платформы и локали, начните с необязательного первого шага.

  1. (Опционально) Если вы хотите сделать свою матрицу конфигураций тестовых устройств, выполните следующие шаги:
  1. Кликните кнопку около Matrix Configuration, чтобы открыть окно определения Matrix Configurations.
  2. Кликните иконку Add New Configuration для добавления новой Матрицы, затем сконфигурируйте ее следующим образом:
  1. Введите имя вашей новой конфигурации в поле Name.
  2. Выберите устройства, версии Android, локали, ориентацию экрана на которых вы хотите протестировать ваше приложение. Например, выберите любые устройства, локали и 2-3 последних уровня API.
  3. Кликните OK для сохранения конфигурации - после этого она должна автоматически выбраться в диалоге "Choose Device".
  1. Кликните OK в диалоге Run/Debug Configurations для выхода.

Запускаем новую конфигурацию запуска и проверяем результаты

  1. Кликните на кнопке Run для тестирования вашего приложения на выбранных конфигурациях. Проект будет собран и автоматически пройдет тесты на каждом устройстве из тестовой матрицы в облаке.
  2. Через несколько минут результаты тестов вам покажут в Android Studio. Результаты всех тестов также можно посмотреть на сайте в разделе Test Lab вашего проекта в консоли Firebase, как показано ниже.

    Пример: Результаты работы Firebase Test Lab в Android Studio

    Пример: Результаты работы Firebase Test Lab в консоли Firebase

Вы использовали Firebase для того, чтобы быстро и просто создать приложение для чата, работающее в реальном времени.

Что мы использовали

Следующие шаги

Узнайте больше