# Интеграция в мобильные приложения Android (v3)

В данной статье представлена подробная инструкция по интеграции **Webim Mobile SDK** версии 3 в ваше Android-приложение. C исходным кодом **Webim Mobile SDK** можно ознакомиться в [репозитории](https://github.com/webim/webim-android-sdk-demo), публикации Android package загружаются в [репозиторий Maven](https://mvnrepository.com/artifact/ru.webim.sdk/webimclientsdkandroid).

!!! Note "N.B."
    При использовании **Webim Mobile SDK** без готового виджета отображение и обработка ссылок в интерфейсе чата выполняются на стороне приложения.

## Шаг 1. Установка библиотеки Webim Mobile SDK

  1. Откройте файл `build.gradle` приложения (модуля).
    Укажите адрес репозитория и добавьте следующий код:

    ```
    repositories {
        mavenCentral()
    }

    dependencies {
        ...
        implementation 'ru.webim.sdk:webimclientsdkandroid:3.+'
    }
    ```

    Для того, чтобы изменения применились, необходимо синхронизировать проект.
    Например, в **Android Studio** можно нажать **Sync Now** или выбрать в меню **File → Sync Progect with Gradle Files**. Дождитесь окончания синхронизации.

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

  1. Для корректной работы SDK укажите его версию в файле `gradle.properties`, добавив параметр:

     ```
     sdkVersionName=3.X.X
     ```

    Замените `3.X.X` на актуальную используемую версию SDK.

  1. Редактируйте файл `AndroidManifest.xml`.
    Необходимо добавить в Ваш манифест следующие элементы:
    в корень `manifest` необходимо добавить разрешение:

    ```
    <uses -permission android:name="android.permission.INTERNET">
    ```

    для хранения истории чата на устройстве следует добавить следующие зависимости:

    ```
    <uses -permission android:name="android.permission.READ_EXTERNAL_STORAGE">
    <uses -permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
    ```

    при необходимости предотвращения перехода устройства в спящий режим для того, чтобы всегда получать сообщения из чата, добавляется следующая строка:

    ```
    <uses -permission android:name="android.permission.WAKE_LOCK">
    ```

---

## Шаг 2. Работа с Webim Mobile SDK

### Работа с сессией { #work-with-session }

После внедрения зависимости в приложение необходимо создать сессию. Сессия в **Webim Mobile SDK** является отдельной сущностью от `VisitSession`, которая реализуется на **Webim Server**.

Сессией называют сеанс, в течение которого приложение взаимодействует с сервисом. В текущей версии SDK он получает любую информацию от **Webim Server** только в рамках сессии. До её создания SDK не получит с сервера никакой информации.
Началом сессии считается выполнение метода `resume()`, а завершением — выполнение метода `destroy()`, либо аварийная остановка приложения.

![Список методов Android SDK Webim в документации](https://webim.ru/kb/img/mobile/sdk/v-3-0/android-integration/sdk-methods.svg)

Но для того, чтобы начать выполнять действия с сессией, необходимо сначала создать объект сессии.

  1. **Создание сессии**. Объект сессии создаётся методом `newSessionBuilder` класса **Webim**, который возвращает объект класса-строителя `SessionBuilder` (оба класса и их методы описаны в файле `Webim.java`). После задания всех необходимых параметров на созданном объекте `SessionBuilder` вызывается метод `build()`, который возвращает объект `WebimSession`. Пример использования:

    ```
    WebimSession webimSession = Webim.newSessionBuilder()
        .setAccountName(ACCOUNT_NAME)
        .setLocation(LOCATION_NAME)
        .build()
    ```

    Поле `ACCOUNT_NAME` должно соответствовать имени аккаунта, если чат размещён в облачной конфигурации (на серверах **Webim** — см. [Сетевые конфигурации сервиса Webim](https://webim.ru/kb/devops/webim-network-configurations.html)). Если же чат размещён на серверах заказчика, то вместо него должен быть указан полный URL (пример: `https://chat.company.ru`).

    Поле `LOCATION_NAME` содержит наименование размещения. Если в нём указать несуществующее значение, то обращение будет в размещении `default`.

    Вызов метода `build()` завершает установку начальных параметров и создаёт сессию в приостановленном виде.

    Описание всех параметров, которые можно задать при создании сессии, а также ошибок, которые могут возникать в процессе, можно найти в [справочнике](https://webim.ru/kb/mobile/sdk/v-3-0/android-sdk-handbook.html#session-builder). Обязательными из них являются только два – имя аккаунта и размещение.

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

  1. **Методы сессии**. Ниже описаны базовые методы, которые необходимо вызывать при работе с объектом сессии.

    | Метод                           | Описание                                                                                                                                                                                                                                                                                                                                                                     | Следующий метод                                                                               |
    | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
    | `resume`                        | После создания объекта сессии необходимо вызвать метод `resume()`, который запустит сетевую активность сессии. После ответов сервера могут быть вызваны методы интерфейсов `MessageListener`, `ChatStateListener`, `CurrentOperatorChangeListener` и `LocationSettingsChangeListener`. Этот метод также используется для возобновления сессии после вызова метода `pause()`. | `pause()`<br>`destroy()`<br>`destroyWithClearVisitorData()`                                   |
    | `pause()`                       | Метод, который используется для приостановки сетевой активности сессии (например, когда пользователь не находится в активном окне приложения). Если сессия уже находится в приостановленном состоянии, метод не произведёт никаких действий.                                                                                                                                 | `resume()`<br>`destroy()`<br>`destroyWithClearVisitorData()`                                  |
    | `destroy()`                     | Метод, который используется для деактивации сессии и экземпляра класса.                                                                                                                                                                                                                                                                                                      | После вызова данного метода никакие методы, относящиеся к сессии, использованы быть не могут. |
    | `destroyWithClearVisitorData()` | Метод, который используется для деактивации сессии и экземпляра класса с удалением информации о пользователе.                                                                                                                                                                                                                                                                | После вызова данного метода никакие методы, относящиеся к сессии, использованы быть не могут. |

    Примеры использования:

    ```
    public class MainActivity extends AppCompatActivity {
      private WebimSession session;
      private static final String ACCOUNT_NAME = "account";
      private static final String LOCATION_NAME = "mobile";
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          webimSession = Webim.newSessionBuilder()
              .setAccountName(ACCOUNT_NAME)
              .setLocation(LOCATION_NAME)
              .build()
      }
      protected void onStart() {
          super.onStart();
          session.resume();
      }
      protected void onResume() {
          super.onResume();
          session.resume();
      }
      protected void onPause() {
          super.onPause();
          session.pause();
      }
      protected void onDestroy() {
          session.destroy();
          super.onDestroy();
      }
    }
    ```

---

### Установка интерфейсов

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

  1. `MessageStream` необходим для непосредственного взаимодействия с сервисом **Webim** (например, для отправки или получения сообщений). Возвращается методом `getStream()` экземпляра `WebimSession`. Примеры использования:

    * Отправить сообщение

        ```  
        session.getStream().sendMessage(message);
        ```

    * Удалить сообщение

        ```
        session.getStream().deleteMessage(message, callback);
        ```

    * Отследить изменения оператора текущего чата

        ```
        session.getStream().setCurrentOperatorChangeListener(
                new MessageStream.CurrentOperatorChangeListener() {
                @Override
                public void onOperatorChanged(@Nullable Operator oldOperator,
                                        @Nullable Operator newOperator) {
        //your code
               }
           });
        ```

    * Отследить изменения статуса сессии

        ```
        session.getStream().setVisitSessionStateListener(
                new MessageStream.VisitSessionStateListener() {
                @Override
                public void onStateChange(@NonNull MessageStream.VisitSessionState previousState,
                                        @NonNull MessageStream.VisitSessionState newState) {
        //your code
                }
            });
        ```

    * Отследить изменения состояния текущего чата

        ```
        session.getStream().setChatStateListener(
                new MessageStream.ChatStateListener() {
                @Override
                public void onStateChange(@NonNull MessageStream.ChatState oldState,
                                        @NonNull MessageStream.ChatState newState) {
        //your code
                }
            });
        ```

    Список всех методов, а так же более подробную информацию о них можно найти в описании метода [`MessageStream`](https://webim.ru/kb/mobile/sdk/v-3-0/android-sdk-handbook.html#message-stream).

  1. `MessageListener` необходим для получения изменений в потоке сообщений. Его методы вызываются автоматически при осуществлении действий с сообщениями в чате: при добавлении сообщения, удаления сообщения, удаления всех сообщений и изменении сообщения, соответственно. В пользовательском приложении необходим класс, который реализует методы данного интерфейса: `messageAdded(Message, Message)`, `messageRemoved(Message)`, `allMessagesRemoved()`, `messageChanged(Message, Message)`.

    Пример использования:

    ```
    public class MainActivity extends AppCompatActivity {
        private WebimSession session;
        private ListController listController;
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            webimSession = Webim.newSessionBuilder()
                .setAccountName(ACCOUNT_NAME)
                .setLocation(LOCATION_NAME)
                .build()
            initChatView(rootView);
        }
        private void initChatView(View rootView) {
            RecyclerView recyclerView = rootView.findViewById(R.id.chat_recycler_view);
            listController = ListController.install(this, recyclerView);
        }
        private static class ListController implements MessageListener {
            private static final int MESSAGES_PER_REQUEST = 25;
            private final RecyclerView recyclerView;
            private final MessagesAdapter adapter;
            static ListController install(WebimChatFragment webimChatFragment,
                                          RecyclerView recyclerView) {
                return new ListController(webimChatFragment, recyclerView);
            }
            private ListController(final WebimChatFragment webimChatFragment,
                                   final RecyclerView recyclerView) {
                this.recyclerView = recyclerView;
                this.adapter = new MessagesAdapter(webimChatFragment);
                this.recyclerView.setAdapter(this.adapter);
            }
            @Override
            public void messageAdded(@Nullable Message before, @NonNull Message message) {
                int index = (before == null) ? 0 : adapter.indexOf(before);
                if (index < = 0) {
                    adapter.add(message);
                    adapter.notifyItemInserted(0);
                } else {
                    adapter.add(index, message);
                    adapter.notifyItemInserted(adapter.getItemCount() - index - 1);
                }
            }

            @Override
            public void messageRemoved(@NonNull Message message) {
                int index = adapter.indexOf(message);
                if (index != -1) {
                    adapter.remove(index);
                    adapter.notifyItemRemoved(adapter.getItemCount() - index);
                }
            }

            @Override
            public void messageChanged(@NonNull Message from, @NonNull Message to) {
                int index = adapter.lastIndexOf(from);
                if (index != -1) {
                    adapter.set(index, to);
                    adapter.notifyItemChanged(adapter.getItemCount() - index - 1, 42);
                }
            }

            @Override
            public void allMessagesRemoved() {
                int size = adapter.getItemCount();
                adapter.clear();
                adapter.notifyItemRangeRemoved(0, size);
            }
        }
    }
    ```

    Список всех методов, а также полную информацию о них можно найти в описании [`MessageListener`](https://webim.ru/kb/mobile/sdk/v-3-0/android-sdk-handbook.html#message-listener).

  1. `MessageTracker` необходим для получения истории сообщений, которая хранится на локальном устройстве. На сервере же история сообщений хранится без ограничений по времени. Объект класса `MessageTracker` можно получить через метод `newMessageTracker(MessageListener)` объекта `MessageStream`:

    ```
    MessageTracker tracker = session.getStream.newMessageTracker(this);
    ```

    Метод `newMessageTracker(MessageListener)` объекта `MessageStream` создает объект класса `MessageTracker`, с помощью которого можно управлять потоком сообщений в контексте приложения клиента. Для получения последних сообщений из истории можно использовать метод `getLastMessages(int limit, GetMessagesCallback callback)`, но не больше указанного лимита.

    Пример использования метода:

    ```
    int MESSAGES_PER_REQUEST = 25;
    tracker.getLastMessages(MESSAGES_PER_REQUEST, new MessageTracker.GetMessagesCallback() {
                    @Override
                    public void receive(@NonNull final List< ? extends Message> received) {
                        //your code
                    }
                });
    ```

    Для дальнейшей итерации по истории сообщений вызывается метод `getNextMessages(int limit, GetMessagesCallback)` — он запрашивает определенное количество сообщений из истории.

    Пример использования метода:

    ```
    int MESSAGES_PER_REQUEST = 25;
    tracker.getNextMessages(MESSAGES_PER_REQUEST, new MessageTracker.GetMessagesCallback() {
                    @Override
                    public void receive(@NonNull final List< ? extends Message> received) {
                        //your code
                    }
                });
    ```

    `Message` необходим для хранения информации о сообщениях. С помощью методов экземпляров класса `Message` можно получить все необходимые данные о конкретном сообщении: уникальный номер сообщение (метод `getId()`), текст сообщения (метод `getText()`), информацию о вложенном файле (метод `getAttachment()`) и т.д. Методы интерфейса `MessageListener` оперируют объектами класса `Message`. Все связанные с этим классом инструменты (методы для работы с вложениями, типы сообщений и пр.) описаны [здесь](https://webim.ru/kb/mobile/sdk/v-3-0/android-sdk-handbook.html#message).

    Подробное описание этих и других методов можно найти в [справочнике методов для Android](https://webim.ru/kb/mobile/sdk/v-3-0/android-sdk-handbook.html).

    !!! Note "N.B."
        Сервис Webim не поддерживает загрузку и отображение изображений в форматах **HEIC/HEIF**: такие файлы будут отклонены сервером, даже если расширения `heic`/`heif` добавлены в `allowed_upload_file_types` на стороне аккаунта. При реализации отправки файлов в Android-приложении необходимо либо исключить выбор файлов этих форматов, либо конвертировать изображение в поддерживаемый формат (например, `jpeg` или `png`) до отправки через Webim Mobile SDK.

## Шаг 3. Прочие настройки

Кроме установки перечисленных ранее интерфейсов для корректной работы приложения на базе **Webim Mobile SDK** необходимо произвести ряд настроек в коде приложения.

  1. Необходимо правильно настроить процедуру составления `visitor fields` и подсчёта `hash`.
  Приватный ключ аккаунта не должен храниться на устройстве или где бы то ни было, кроме сервера заказчика. В противном случае появляется серьёзная угроза безопасности и риск утечки переписок клиента третьим лицам.

    Пример передачи `json-encoded` объекта посетителя SDK:

    ```
    .setVisitorFieldsJson("{
        "fields": {
          "id":"1234567890987654321",
          "display_name":"Никита"
        },
        "hash":"ffadeb6aa3c788200824e311b9aa44cb"
    }")
    ```

    Подробно об идентификации авторизованных клиентов можно почитать в [этой статье](https://webim.ru/kb/dev/identification/id-2-0.html).

  1. Для приложения на базе SDK может быть использован обфускатор. В этом случае необходимо задавать исключения в правилах обфускатора, чтобы определённые классы не обфусцировались — это необходимо для корректной работы приложения на базе **Webim** Mobile SDK. В частности, при использовании обфускатора **Proguard** следует задать в его правилах следующее:

    ```
    -keep public class ru.webim.android.sdk.* { *; }

    # when using ru.webim.sdk:chatview dependency

    -keep class ru.webim.chatview.** { *; }
    ```

    Это будет означать, что указанный пакет/класс не будет "шифроваться" обфускатором.

  1. Обеспечить необходимые сетевые доступы.

    Для мобильного устройства, на котором установлено мобильное приложение, интегрированное с **Webim Mobile SDK**, должны быть открыты исходящие порты для следующих URL-адресов:

      * `https://*.webim.ru/l/v/m/action 443 (POST)` - используется для передачи действий от клиентов.

      * `https://*.webim.ru/l/v/m/init 443 (GET)` - используется для инициализации сессии

      * `https://*.webim.ru/l/v/m/delta 443 (GET)` - используется для получения инкрементальных обновлений.

      * `https://*.webim.ru/l/v/m/download 443 (GET)` (не используется в SDK, но нужно для скачивания файлов в мобильном приложении)

      * `https://*.webim.ru/l/v/m/upload 443 (POST)` - используется для отправки файлов с клиента на сервер.

      * `https://*.webim.ru/l/v/file-delete 443 (GET)` - используется для удаления файлов клиента с сервера.

      * `https://*.webim.ru/l/v/m/history 443 (GET)` - используется для получения истории чата из базы данных.

      * `https://*.webim.ru/l/v/m/search-messages 443 (GET)` - используется для поиска по истории сообщений.

      * `https://*.webim.ru/l/v/get-online-status 443 (GET)` - используется для получения информации о статусе размещения.

      * `https://*.webim.ru/api/visitor/v1/configs/* 443 (GET)` - используется получения конфигурации аккаунта.

      * `https://*.webim.ru/l/v/m/auth 443 (POST)` - используется для аутентификации посетителя.

      * `https://*.webim.ru/l/v/m/info 443 (GET)` - используется для получения информации без создания сессии.

      * `https://*.webim.ru/api/visitor/meta 443 (GET)` - используется для получения истории чата из базы данных.

    Если чат размещён на серверах заказчика, то домен должен быть изменен на их собственный.

    Для отправки push-уведомлений FCM вашей сети потребуется взаимодействовать со следующими серверами по протоколу HTTPS:

      * `fcm.googleapis.com`

      * `accounts.google.com`

      * `iid.googleapis.com`

    Данные порты должны быть доступны: 443, 5228, 5229, 5230.

    С более подробной информацией можно ознакомиться на сайте [Firebase](https://firebase.google.com/docs/cloud-messaging/network-configuration).

  1. Для того, чтобы после завершения чата пользователь мог поставить оценку работы оператора, необходимо описать соответствующий механизм в приложении. SDK не реализует механизм оценки, а получает её от приложения клиента.

  1. Включены и настроены push-уведомления (опционально). Подробнее параметры push-уведомлений и примеры можно посмотреть в статье [Push-уведомления](https://webim.ru/kb/mobile/push-notifications/).

  1. Для начала чата может вызываться метод `startChat()`. Если метод не будет вызван, чат будет начат после первого сообщения посетителя.

---

Источник: https://webim.ru/kb/mobile/sdk/v-3-0/android-integration.html
