How-To指南:與 Graph 通知整合 (Android)

Graph 通知可讓應用程式跨多個裝置來傳送及管理使用者鎖定的通知。

當 Android 上有 Project Rome 用戶端 SDK 時,Android 應用程式就可以進行註冊,以接收應用程式伺服器發佈給已登入使用者的通知。 此 SDK 可讓應用程式用戶端接收新的傳入通知承載、管理現有通知的狀態,以及擷取通知歷程記錄。 如需通知本身及其如何能夠以人為中心來傳遞通知的詳細資訊,請參閱 Microsoft Graph 通知概觀

Project Rome SDK 中的所有功能 (包括 Graph 通知等) 會以稱為連線裝置平台的基礎平台作為建置基礎。 本指南旨在帶領您完成必要步驟以便開始使用連線裝置平台,並說明如何取用 SDK 中的 API 來實作 Graph 通知專用的功能。

請參閱 API 參考頁面,來取得通知案例相關參考文件的連結。

下列步驟會參考來自 Project Rome Android 應用程式範例的程式碼。

針對所有連線裝置功能,您需要 Android 應用程式開發 IDE 和具有其中一個支援架構的 Android 裝置 (armeabi-v7aarm64-v8ax86x86_64) 或模擬器。 系統必須執行 Android 4.4.2 或更新版本。

連線裝置平台和通知的預備設定

在實作遠端連線之前,您必須先採取幾個步驟,以便讓 Android 應用程式能夠連線到遠端裝置以及傳送和接收通知。

註冊您的應用程式

Project Rome SDK 的功能幾乎都需要 Microsoft 帳戶 (MSA) 或 Azure Active Directory (AAD) 驗證 (鄰近分享 API 除外)。 如果您還沒有 MSA 但想要使用,請在 account.microsoft.com 上註冊。

注意

裝置轉送 API 則不支援 Azure Active Directory (AAD) 帳戶。

使用所選擇的驗證方法時,您必須遵循應用程式註冊入口網站上的指示,向 Microsoft 註冊應用程式。 如果您沒有 Microsoft 開發人員帳戶,則必須建立一個。

當您使用 MSA 註冊應用程式時,應該會收到用戶端識別碼字串。 請儲存此字串以供稍後使用。 此字串可讓應用程式存取 Microsoft 的連線裝置平台資源。 如果您使用 AAD,請參閱 Azure Active Directory 驗證程式庫來找到有關如何取得用戶端識別碼字串的指示。

新增 SDK

請將下列存放庫參考插入到位於專案根目錄的 build.gradle 檔案。

allprojects {
    repositories {
        jcenter()
    }
}

然後,將下列相依性插入到位於專案資料夾內的 build.gradle 檔案。

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

在專案的 AndroidManifest.xml 檔案中,於 <manifest> 元素內新增下列權限 (如果其中還沒有這些權限)。 這可讓應用程式獲得權限而能夠連線至網際網路,並在裝置上啟用藍牙探索。

請注意,只有在使用藍牙探索時才需要藍牙相關權限;連線裝置平台中的其他功能則不需要這些權限。 此外,只有在 Android SDK 21 及更新版本上才需要 ACCESS_COARSE_LOCATION。 在 Android SDK 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 介面,請將下列資訊記下來:

如果您使用 MSA,則必須在登入要求中加入下列領域︰"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"

注意

裝置轉送 API 則不支援 Azure Active Directory (AAD) 帳戶。

無論您是否使用提供的ConnectedDevicesAccountManager實作,如果您使用 AAD,則必須在 azure Active Directory > Azure 入口網站 (portal.azure.com 應用程式註冊) > 上的應用程式註冊中指定下列許可權:

  • Microsoft 活動摘要服務
    • 傳遞並修改此應用程式的使用者通知
    • 在使用者的活動摘要中讀取及寫入應用程式活動
  • Windows 通知服務
    • 將裝置連線到 Windows 通知服務
  • Microsoft 裝置目錄服務
    • 查看裝置清單
    • 務必新增至裝置和應用程式的清單
  • Microsoft 命令服務
    • 與使用者裝置通訊
    • 讀取使用者裝置

針對推播通知註冊應用程式

向 Google 註冊應用程式以獲得 Firebase 雲端通訊支援。 請務必記下您所收到的寄件者識別碼和伺服器金鑰;稍候需要用到這些資料。

註冊之後,您必須讓推播通知功能與應用程式中的連線裝置平台相關聯。

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 裝置提出要求時,才需要執行這個步驟。 如果您只以 Windows 裝置作為目標,則不需要完成此步驟。

請移至開發人員中心儀表板、從左側瀏覽窗格瀏覽至跨裝置體驗,然後選取要設定新的跨裝置應用程式,如下所示。 開發人員中心儀表板 – 跨裝置體驗

開發人員中心的上線程序需要執行下列步驟:

  • 選取支援的平台 – 選取會出現應用程式並為應用程式啟用跨裝置體驗的平台。 在 Graph 通知整合的案例中,您可以選取 Windows、Android 及/或 iOS。 跨裝置體驗 – 支援的平臺

  • 提供應用程式識別碼 – 針對會出現應用程式的每個平台提供應用程式識別碼。 若為 Android 應用程式,此識別碼是您在建立專案時指派給應用程式的套件名稱。 您可以在 Firebase 主控台的 [專案概觀 - > 一般] 底下找到套件名稱。 您可以為每個平台新增不同的識別碼 (最多十個) – 這是為了讓同一個應用程式的多個版本 (或甚至是不同的應用程式),能夠接收應用程式伺服器傳送給同一位使用者的相同通知。 跨裝置體驗 – 應用程式識別碼

  • 請提供或選取來自 MSA 和/或 AAD 應用程式註冊的應用程式識別碼。 這些對應至 MSA 或 AAD 應用程式註冊的用戶端識別碼,會從上面先前的 MSA/AAD 應用程式註冊步驟取得。 跨裝置體驗 – MSA 和 AAD 應用程式註冊

  • Graph 通知和其他連線裝置平台功能會利用主要平台上的每個原生通知平台,將通知往下傳送給應用程式用戶端端點,也就是 WNS (適用於 Windows UWP)、FCM (適用於 Android) 和 APNS (適用於 iOS)。 請提供這些通知平台的認證,以便在您發佈目標為使用者的通知時,Graph 通知能夠為應用程式伺服器傳遞通知。 在 Android 中,您必須先啟用雲端通訊服務才能使用 Microsoft Graph 通知。 也請注意,所需的寄件者識別碼會對應至 Firebase 雲端通訊寄件者識別碼,API 金鑰則會對應至舊版伺服器金鑰。 這兩者都可以在 Firebase 主控台 - > 專案 - > 設定的 [雲端通訊] 索引標籤底下找到,如螢幕擷取畫面所示。 跨裝置體驗 – 推送認證

  • 最後一個步驟是驗證跨裝置應用程式網域,這可作為驗證程序來證明應用程式具有此網域的擁有權,其作用類似已註冊應用程式的跨裝置應用程式身分識別。 跨裝置體驗 – 網域驗證

初始化 Graph 通知通道

Project Rome SDK 可讓應用程式訂閱不同通道,以便接收及管理各種類型的使用者資料,包括 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 來接收傳入的 UserNotification 並存取 UserNotification 歷程記錄

和之前的示範一樣,這個抵達應用程式用戶端的初始 Google 雲端通訊通知只包含拍肩提醒,您必須將該拍肩提醒承載傳遞到連線裝置平台,才能觸發 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 雲端通訊的資料訊息在 OS 層級的通知匣中建構視覺通知,或如果您使用通知中的內容來更新某些應用程式內 UI,便可使用此事件接聽程式來進行相關操作。

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 變更更新,往往是由應用程式用戶端所起始 (從不同的裝置)。 您可以選擇要更新 UserNotification 狀態的時間,但其更新時機通常是對應的視覺通知是由該裝置上的使用者處理時,或使用者在您啟用的某些應用程式內體驗進一步處理通知時。 以下是流程外觀的範例:您的應用程式伺服器會發佈以使用者 A 為目標的通知。使用者 A 會在其電腦及其安裝應用程式用戶端的手機上收到此通知。 使用者在電腦上按一下通知,並進入應用程式來處理對應的工作。 此電腦上的應用程式用戶端隨即會呼叫連線裝置平台 SDK 來更新對應 UserNotification 的狀態,以便讓此更新同步處理到這位使用者的所有裝置。 其他應用程式用戶端則會在收到此狀態更新時,即時地從裝置的通知中心/通知匣/控制中心移除對應的視覺警示/訊息/快顯通知。 這便是通知在使用者的所有裝置上全面關閉的方式。

提示

UserNotification 類別目前提供 2 種狀態更新,您可以修改 UserNotificationReadState 或 UserNotificationUserActionState 並定義您自己的邏輯來指出通知有所更新時應該如何處理。 例如,您可以將 UserActionState 標示為已啟用或已關閉,並根據該值來實作通用關閉。 或者,您也可以同時將 ReadState 標示為已讀取或未讀取,並據此決定應該在應用程式內的通知歷程記錄檢視中顯示哪些通知。 下列程式碼片段會示範如何將通知的 UserNotificationUserActionState 標示為已關閉。

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