Поделиться через


Практическое руководство. Интеграция с уведомлениями Graph (Android)

Служба уведомлений Graph позволяет приложению отправлять уведомления, предназначенные для пользователей на нескольких устройствах, а также управлять этими уведомлениями.

Используя клиентский пакет SDK для Project Rome в Android, ваше приложение Android может зарегистрироваться для получения уведомлений, публикуемых с сервера приложений, которые предназначены для вошедшего в систему пользователя. Этот пакет SDK позволяет клиенту приложения получать полезные данные новых входящих уведомлений, управлять состоянием существующих уведомлений и получать журнал уведомлений. Дополнительные сведения о службе уведомлений и ее использовании для доставки уведомлений, предназначенных для пользователей, см. в разделе Microsoft Graph Notifications (уведомления Microsoft Graph).

Все компоненты в пакете SDK для Project Rome, включая уведомления Graph и многое другое, созданы на основе базовой платформы, которая называется платформой подключенных устройств. Это руководство ознакомит вас с тем, что необходимо сделать, чтобы приступить к использованию платформы подключенных устройств. В нем объясняется, как использовать интерфейсы API в пакете SDK для реализации функций уведомлений Graph.

На странице Справочник по API приведены ссылки на справочную документацию, относящуюся к сценариям с использованием уведомлений.

В приведенных ниже инструкциях используется код из примера приложения Android для Project Rome.

Для использования всех возможностей подключенных устройств потребуется интегрированная среда разработки приложений Android и устройство Android, использующее одну из поддерживаемых архитектур (armeabi-v7a, arm64-v8a, x86 или x86_64), или эмулятор. Система должна работать под управлением Android 4.4.2 или более поздней версии.

Предварительная настройка для платформы подключенных устройств и уведомлений

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

Регистрация приложения

Аутентификация с помощью учетных записей Майкрософт (MSA) или Azure Active Directory (AAD) является обязательной почти для всех компонентов пакета SDK для Project Rome (за исключением интерфейсов API обмена с устройствами поблизости). Если у вас нет учетной записи Майкрософт и вы хотите ее использовать, зарегистрируйтесь на сайте account.microsoft.com.

Примечание

Учетные записи Azure Active Directory (AAD) не поддерживаются интерфейсами API Device Relay.

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

При регистрации приложения с помощью учетной записи Майкрософт (MSA) вы должны получить строку идентификатора клиента. Сохраните ее последующего использования. Она позволит вашему приложению обращаться к ресурсам платформы подключенных устройств корпорации Майкрософт. Если вы используете AAD, обратитесь к разделу Библиотеки проверки подлинности Azure Active Directory за инструкциями по получению строки идентификатора клиента.

Добавление пакета SDK

Вставьте приведенные ниже ссылки на репозиторий в файл build.gradle в корне проекта.

allprojects {
    repositories {
        jcenter()
    }
}

Затем вставьте приведенную ниже зависимость в файл build.gradle, который находится в папке проекта.

dependencies { 
    ...
    implementation 'com.microsoft.connecteddevices:connecteddevices-sdk:+'
}

В элемент <manifest> в файле AndroidManifest.xml проекта добавьте следующие разрешения (если они еще не существуют). Это обеспечит приложению разрешение для подключения к Интернету и включения обнаружения Bluetooth на устройстве.

Обратите внимание на то, что разрешения для Bluetooth необходимы только для использования обнаружения Bluetooth. Они не нужны для других возможностей платформы подключенных устройств. Кроме того, ACCESS_COARSE_LOCATION требуется только для пакетов SDK для Android версии 21 и более поздних версий. В пакетах SDK для Android версии 23 и более поздних версиях разработчик должен также предлагать пользователю предоставить доступ к расположению во время выполнения.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

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

import com.microsoft.connecteddevices;
import com.microsoft.connecteddevices.remotesystems;
import com.microsoft.connecteddevices.remotesystems.commanding;

Настройка аутентификации и управление учетными записями

В процессе регистрации платформе подключенных устройств требуется допустимый маркер OAuth. Вы можете использовать предпочтительный способ создания маркеров OAuth и управления ими. Тем не менее, чтобы помочь разработчикам приступить к работе с платформой, мы добавили поставщик аутентификации в пример приложения Android, который создает маркеры обновления и управляет ими.

Если вы хотите реализовать интерфейс ConnectedDevicesAccountManager самостоятельно, запишите следующие сведения.

Если вы используете учетную запись Майкрософт, необходимо включить следующие области в запрос на вход: "wl.offline_access", "ccs.ReadWrite", "dds.read", "dds.register", "wns.connect", "asimovrome.telemetry" и "https://activity.windows.com/UserActivity.ReadWrite.CreatedByApp".

Если вы используете учетную запись AAD, необходимо запросить следующие аудитории: "https://cdpcs.access.microsoft.com", "https://cs.dds.microsoft.com", "https://wns.windows.com/" и "https://activity.microsoft.com".

Примечание

Учетные записи Azure Active Directory (AAD) не поддерживаются интерфейсами API Device Relay.

Вне зависимости от того, применяете вы предоставленную реализацию ConnectedDevicesAccountManager или нет, если вы используете AAD, то потребуется указать следующие разрешения в регистрации приложения на портале Azure (portal.azure.com > Azure Active Directory > "Регистрация приложений"):

  • Microsoft Activity Feed Service:
    • доставка и изменение уведомлений пользователя для этого приложения;
    • чтение и запись действий приложений в веб-канале действий пользователей;
  • Windows Notification Service:
    • подключение устройства к службе уведомлений Windows;
  • Microsoft Device Directory Service:
    • просмотр списка устройств;
    • добавление в список устройств и приложений;
  • Microsoft Command Service:
    • взаимодействие с устройствами пользователей;
    • чтение устройств пользователей.

Регистрация приложения для работы с push-уведомлениями

Зарегистрируйте свое приложение в Google для поддержки Firebase Cloud Messaging. Обязательно запишите идентификатор отправителя и ключ сервера, которые вы получите. Они потребуются вам позже.

После регистрации необходимо связать функции push-уведомлений с платформой подключенных устройств в приложении.

mNotificationRegistration = new ConnectedDevicesNotificationRegistration();
mNotificationRegistration.setType(ConnectedDevicesNotificationType.FCM);
mNotificationRegistration.setToken(token);
mNotificationRegistration.setAppId(Secrets.FCM_SENDER_ID);
mNotificationRegistration.setAppDisplayName("SampleApp");

Регистрация приложения в Центре разработки для Microsoft Windows для работы на нескольких устройствах

Важно!

Это действие необходимо, только если вы хотите использовать возможности Project Rome для доступа к данным на устройствах, не использующих Windows, или для отправки запросов к этим устройствам. Если ваше приложение нацелено только на устройства с Windows, выполнять этот шаг не нужно.

Перейдите к панели мониторинга Центра разработки, выберите "Cross-Device Experiences" (Работа на нескольких устройствах) в области навигации слева и выберите "Configure a new cross-device app" (Настроить новое приложение для нескольких устройств), как показано ниже. Панель мониторинга Центра разработки — работа на нескольких устройствах

Процесс подключения в Центре разработки включает в себя следующие действия.

  • Выберите поддерживаемые платформы. Выберите целевые платформы для своего приложения, поддерживающего работу на нескольких устройствах. В случае интеграции уведомлений Graph можно выбрать Windows, Android и (или) iOS. Работа на нескольких устройствах — поддерживаемые платформы

  • Укажите идентификаторы приложения для каждой из целевых платформ для своего приложения. Для приложений Android это имя пакета, назначенное приложению при создании проекта. Имя пакета можно найти в консоли Firebase в разделе Project Overview (Обзор проекта) > General (Общие). Можно добавить разные идентификаторы (до десяти) для каждой платформы. Эта возможность предоставляется на случай, если у вас есть несколько версий одного приложения или даже разных приложений, которые должны получать одни и те же уведомления от вашего сервера приложений, предназначенные для одного пользователя. Работа на нескольких устройствах — идентификаторы приложения

  • Введите или выберите идентификаторы приложения из данных регистрации приложения для использования учетных записей Майкрософт и (или) AAD. Эти идентификаторы клиента, соответствующие регистрации приложения для использования учетных записей Майкрософт или AAD, были получены на предыдущих шагах регистрации приложения в этих службах, описанных выше. Работа на нескольких устройствах — регистрация приложения для использования учетных записей Майкрософт и AAD

  • Уведомления Graph и другие возможности платформы подключенных устройств используют каждую из собственных платформ уведомлений для основных платформ, чтобы отправлять уведомления на конечные точки клиента приложения, а именно WNS (для приложений UWP для Windows), FCM (для Android) и APNs (для iOS). Укажите свои учетные данные для этих платформ уведомлений, чтобы служба уведомлений Graph могла доставлять уведомления для сервера приложений, когда вы публикуете уведомления, предназначенные для пользователей. Для Android включение службы Cloud Messaging является необходимым условием для использования уведомлений Microsoft Graph. Кроме того, имейте ввиду, что требуемый идентификатор отправителя соответствует идентификатору отправителя Firebase Cloud Messaging, а ключ API соответствует ключу сервера для прежних версий. Оба эти значения можно найти в консоли Firebase, выбрав Project (Проект) > Settings (Параметры), на вкладке Cloud Messaging (Обмен сообщениями в облаке), как показано на снимке экрана. Работа на нескольких устройствах — учетные данные службы push-уведомлений

  • Последним шагом является проверка домена приложения для нескольких устройств. Она позволяет подтвердить, что ваше приложение является владельцем этого домена, который выполняет роль удостоверения приложения на нескольких устройствах для зарегистрированного вами приложения. Работа на нескольких устройствах — проверка домена

Инициализация канала уведомлений Graph

Пакет SDK для Project Rome позволяет вашему приложению подписаться на различные каналы, чтобы получать различного типа данные пользователей и управлять этими данными, включая уведомления Graph, действия пользователей и многое другое. Для их хранения и синхронизации используется UserDataFeed. UserNotification — это класс и тип данных, соответствующий уведомлению для пользователя, отправленного с помощью службы уведомлений Graph. Чтобы интегрировать уведомления Graph и начать прием UserNotification, публикуемых сервером приложений, необходимо сначала инициализировать веб-канал данных пользователя, создав UserNotificationChannel. Это действие следует рассматривать как инициализацию платформы, описанную выше. Оно должно проверяться и, возможно, повторяться каждый раз, когда приложение перемещается на передний план (но не перед инициализацией платформы).

Приведенные ниже методы инициализируют UserNotificationChannel.

private UserNotificationChannel mNotificationChannel;
private UserDataFeed mUserDataFeed;

// ...

/**
 * Initializes the UserNotificationFeed.
 */
public void initializeUserNotificationFeed() {

    // define what scope of data this app needs
    SyncScope[] scopes = { UserNotificationChannel.getSyncScope() };

    // Get a reference to the UserDataFeed. This method is defined below
    mUserDataFeed = getUserDataFeed(scopes, new EventListener<UserDataFeed, Void>() {
        @Override
        public void onEvent(UserDataFeed userDataFeed, Void aVoid) {
            if (userDataFeed.getSyncStatus() == UserDataSyncStatus.SYNCHRONIZED) {
                // log synchronized.
            } else {
                // log synchronization not completed.
            }
        }
    });

    // this method is defined below
    mNotificationChannel = getUserNotificationChannel();
}

// instantiate the UserDataFeed
private UserDataFeed getUserDataFeed(SyncScope[] scopes, EventListener<UserDataFeed, Void> listener) {
    UserAccount[] accounts = AccountProviderBroker.getSignInHelper().getUserAccounts();
    if (accounts.length <= 0) {
        // notify the user that sign-in is required
        return null;
    }

    // use the initialized Platform instance, along with the cross-device app ID.
    UserDataFeed feed = UserDataFeed.getForAccount(accounts[0], PlatformBroker.getPlatform(), Secrets.APP_HOST_NAME);
    feed.addSyncStatusChangedListener(listener);
    feed.addSyncScopes(scopes);
    // sync data with the server
    feed.startSync();
    return feed;
}

// use the UserDataFeed reference to create a UserActivityChannel
@Nullable
private UserNotificationChannel getUserNotificationChannel() {
    UserNotificationChannel channel = null;
    try {
        // create a UserNotificationChannel for the signed in account
        channel = new UserNotificationChannel(mUserDataFeed);
    } catch (Exception e) {
        e.printStackTrace();
        // handle exception
    }
    return channel;
}

На этом этапе в mNotificationChannel должна быть ссылка на UserNotificationChannel.

Создайте UserNotificationReader для получения входящих UserNotifications и доступа к журналу UserNotification.

Как мы показали ранее, начальное автоматическое сообщение Google Cloud Messaging, поступающее в клиент приложения, содержит только сигнал. Необходимо передать полезные данные этого сигнала в платформу подключенных устройств, чтобы активировать пакет SDK и выполнить полную синхронизацию с сервером подключенных устройств, который содержит все объекты UserNotification, опубликованные вашим сервером приложений. Это позволит получить полные полезные данные уведомления, опубликованного сервером приложений и соответствующего этому сигналу (и в случае, если какие-либо уведомления были ранее опубликованы, но не получены на этом клиенте приложения из-за проблем с подключением устройств или других проблем, они также будут получены). С помощью этих непрерывных операций синхронизации в реальном времени, выполняемых пакетом SDK, клиент приложения может обращаться к локальному кэшу веб-канала данных UserNotification данного вошедшего в систему пользователя. В данном случае UserNotificationReader обеспечивает доступ клиента приложения к этому веб-каналу данных для получения последних полезных данных уведомления через прослушиватель событий или для обращения к полной коллекции UserNotification, которую можно использовать как модель представления для журнала уведомлений пользователя.

Получение объектов UserNotification

Сначала необходимо создать экземпляр UserNotificationReader и получить все существующие объекты UserNotification, уже полученные средством чтения, если вы хотите использовать эту информацию для возможностей, которые вы пытаетесь включить. Всегда разумно предполагать, что сервер приложений уже публиковал уведомления для этого выполнившего вход пользователя, учитывая, что эта конкретная конечная точка устройства может быть не единственной или не первой конечной точкой, на которую пользователь установил ваше приложение.

private static UserNotificationReader mReader;
private static final ArrayList<UserNotification> mHistoricalNotifications = new ArrayList<>();
// Instantiate UserNotificationReader
UserNotificationReaderOptions options = new UserNotificationReaderOptions();
mReader = mNotificationChannel.createReaderWithOptions(options);
// Read any previously published UserNotifications that have not expired yet
mReader.readBatchAsync(Long.MAX_VALUE).thenAccept(new AsyncOperation.ResultConsumer<UserNotification[]>() {
    @Override
    public void accept(UserNotification[] userNotifications) throws Throwable {
        synchronized (mHistoricalNotifications) {
            for (UserNotification notification : userNotifications) {
                if (notification.getReadState() == UserNotificationReadState.UNREAD) {
                    mHistoricalNotifications.add(notification);
                }
            }
        }
 
        if (RunnableManager.getHistoryUpdated() != null) {
            activity.runOnUiThread(RunnableManager.getHistoryUpdated());
        }
    }
});

Теперь добавьте прослушиватель событий, который активируется после завершения синхронизации платформой подключенных устройств и уведомляет о новых изменениях. В случае уведомлений Graph новыми изменениями могут быть новые входящие объекты UserNotification, опубликованные сервером приложений, или операции обновления, удаления и истечения срока действия объектов UserNotifcation, которые были выполнены на сервере или других зарегистрированных конечных точках, на которые выполнил вход этот же пользователь.

Совет

Этот прослушиватель событий выполняет основную бизнес-логику и "использует" содержимое полезных данных уведомления в зависимости от ваших сценариев. Если вы сейчас используете сообщение данных Google Cloud Messaging для создания видимого уведомления в центре уведомлений уровня ОС или используете содержимое уведомления для обновления каких-либо элементов пользовательского интерфейса в приложении, это следует делать именно здесь.

mReader.addDataChangedListener(new EventListener<UserNotificationReader, Void>() {
    @Override
    public void onEvent(UserNotificationReader userNotificationReader, Void aVoid) {
        userNotificationReader.readBatchAsync(Long.MAX_VALUE).thenAccept(new AsyncOperation.ResultConsumer<UserNotification[]>() {
        @Override
        public void accept(UserNotification[] userNotifications) throws Throwable {
            boolean updatedNew = false;
            boolean updatedHistorical = false;
            synchronized (sHistoricalNotifications) {
                for (final UserNotification notification : userNotifications) {
                    if (notification.getStatus() == UserNotificationStatus.ACTIVE && notification.getReadState() == UserNotificationReadState.UNREAD) {
                        switch (notification.getUserActionState()) {
                            case NO_INTERACTION:
                                // Brand new notification
                                // Insert business logic to construct a new visual notification in Android notification tray for the user to see
                                // ...
                            case DISMISSED:
                                // Existing notification that is marked as dismissed
                                // An app client receive this type of changes because another app client logged in by the same user has marked the notification as dismissed and the change is fanned-out to everywhere
                                // This state sync across app clients on different devices enable universal dismiss of notifications and other scenarios across multiple devices owned by the same user
                                // Insert business logic to dismiss the corresponding visual notification inside Android system notification tray, to make sure users don’t have to deal with redundant information across devices, and potentially insert this notification in your app’s notification history view
                                // ...
                            default:
                                // Unexpected
                        }
                    } else {
                        // ...
                    }
                }
            }
        }
    });
}
});

Обновление состояния существующего объекта UserNotification

В предыдущем разделе мы уже упоминали, что иногда изменение UserNotification, полученное из средства чтения, может быть обновлением состояния существующего объекта UserNotification, вне зависимости от того, помечен он как "закрытый" или "прочитанный". В этом случае клиент приложения может выбрать, что делать дальше. Например, он может выполнить универсальное закрытие, удалив соответствующее видимое уведомление на этом устройстве. Если вернуться немного назад, часто именно клиент приложения изначально является инициатором этого изменения объекта UserNotification, только на другом устройстве. Можно выбрать время для обновления состояния объектов UserNotifications, но обычно они обновляются, когда пользователь реагирует на соответствующее видимое уведомление на этом устройстве или когда уведомление подвергается дальнейшей обработке пользователем в специально реализованном вами интерфейсе приложения. Ниже приведен пример такого потока операций. Сервер приложений публикует уведомление, адресованное пользователю А. Пользователь A получает это уведомление на своем компьютере и телефоне, на которых установлены клиенты приложения. Пользователь щелкает это уведомление на компьютере и переходит в приложение, чтобы выполнить соответствующую задачу. Клиент приложения на этом компьютере отправит вызов к пакету SDK платформы подключенных устройств, чтобы обновить состояние соответствующего объекта UserNotification. Это позволит синхронизировать это обновление на всех устройствах данного пользователя. Другие клиенты приложения, получив это обновление состояния в реальном времени, удалят соответствующее видимое оповещение, сообщение или всплывающее уведомление из центра уведомлений, области уведомлений или центра действий на устройстве. Так происходит универсальное закрытие уведомлений на устройствах пользователя.

Совет

Класс UserNotification в настоящее время предоставляет два типа обновления состояния. Вы можете изменить UserNotificationReadState или UserNotificationUserActionState и определить собственную логику, выполняемую после обновления уведомлений. Например, можно помечать состояние UserActionState как "активированное" или "закрытое" и выводить сводку по этому значению, чтобы реализовать универсальное закрытие. В качестве альтернативного или дополнительного метода можно помечать состояние ReadState как "прочитанное" и "непрочитанное", и на основе этого состояния определять, какие уведомления должны отображаться в представлении журнала уведомлений в приложении. Ниже приведен фрагмент кода, в котором показано, как пометить состояние UserNotificationUserActionState уведомления как "Dismissed" (Закрыто).

public void dismissNotification(int position) {
    final UserNotification notification = mNewNotifications.get(position);
          
    notification.setUserActionState(UserNotificationUserActionState.DISMISSED);
    notification.saveAsync();
}