デスクトップとモバイルデバイスでのプッシュ通知
デスクトップとモバイルのアプリケーションでローカルとリモートの通知をトリガーします。リモートサーバーからmacOS/iOSとAndroidデバイスの両方にプッシュ通知を送信する方法をご紹介します。
レベル:上級
プラットフォーム:macOS, iOS, Android
クラス: PushNotifications::Listener,PushNotifications::Notification,PushNotifications::Channel,PushNotifications::Settings
このプロジェクトには、macOS/iOSではApple Developerアカウント、AndroidではGoogle Firebaseアカウントが必要です。ヘルプが必要な場合はApple DeveloperそしてGoogle Firebaseのウェブサイトで口座を開設する。
シミュレーターはリモート・テストをサポートしていないため、このプロジェクトではプッシュ通知をテストするために物理的なデバイスが必要です。このためにデバイスを用意してください。
プッシュ通知でサポートされる機能は、プラットフォームやOSのバージョンによって異なります。macOSの場合、リモート通知には最低10.7、ローカル通知には最低10.8が必要です。Androidの場合、リモート通知には最低14(Ice Cream Sandwich)のAPIが、通知チャンネルには最低27のAPIが必要です。チュートリアルではSDKの最低バージョンを23としていますが、14以上に変更することも歓迎します。
スタート
このチュートリアルのデモ・プロジェクトのダウンロードはこちらから:PIP|ZIP.プロジェクトを解凍し、最初のヘッダーファイルをProjucerで開く。
このプロジェクトのpipバージョンを使用する場合は、必ずResourcesフォルダを生成されたProjucerプロジェクトに追加します。
このステップでヘルプが必要な場合は、以下を参照してください。Tutorial: Projucer Part 1: Getting started with the Projucer.
デモ・プロジェクト
このプロジェクトは、ローカルとリモートの両方の通知を送受信するための包括的なユーザー・インタフェースを提供します。通知パラメーターと識別子を整理するために、別々のテーブルが表示されます。iOSシミュレーターでモバイル・アプリケーションを実行すると、ウィンドウは次のようになるはずです:

ここで紹介するコードは、大まかに以下のものと似ている。プッシュ通知デモJUCE Examplesより。
初期設定
このプロジェクトが正しく機能するためには、特定のデプロイメント・プラットフォームに適した開発者コンソールで、いくつかの初期設定手順を実行する必要がある。まず、Projucerでプッシュ通知の適切なパーミッションを許可しよう。macOSとiOSではプッシュ通知機能フィールドに移動します。Androidではリモート通知フィールドにいる。


また、以 下のように実行ファイルにリソースをバンドルするために、macOSとiOSの両方でカスタムXcodeリソースフォルダを指定する必要があります:


最後に、Androidの場合、「Debug」と「Release」の両方のエクスポーター設定で、実行ファイルに追加したい各リソースをrawリソースとして、以下のように個別に指定する必要がある:


必要なリソースのリストは以下の通り:
../Resources/sounds/demonstrative.mp3
../Resources/sounds/isntit.mp3
../Resources/sounds/jinglebellssms.mp3
../Resources/sounds/served.mp3
../Resources/sounds/solemn.mp3
../Resources/images/ic_stat_name.png
../Resources/images/ic_stat_name2.png
../Resources/images/ic_stat_name3.png
../Resources/images/ic_stat_name4.png
../Resources/images/ic_stat_name5.png
プロジェクトを保存してお気に入りのIDEで開くと、Projucerが自動的に必要なエンタイトルメントをデプロイメント・ターゲットに追加します。
Apple Developer
アンドロイド用に開発する場合は、次のセクションに進んでください。Google Firebaseをご覧ください。
macOSとiOSでは、アプリケーションに署名するために、Xcode内のApple Developerアカウントでサインインし、開発チームを選択する必要があります。あなたのプロジェクトのためのユニークなバンドルIDを選択します。以下のスクリーンショットのように、Xcodeは自動的に署名証明書とProvisioning Profileを提供します:

また、正しいアプリの機能がチェックされ、承認されていることも確認してください。能力設定ウィンドウを開きます。以下のように同じ情報が表示されるはずです(macOSにはBackground Modesのセクションがないことに注意してください):

Pusher
iOSのリモート通知をテストするには、Apple Push Notification Service(APN)を使って通知を送信するサーバーか アプリケーションが必要です。GoogleのFirebaseを利用してテストすることも可能だが(Android側と同じように)、ありがたいことに、以下のようなアプリを利用することで、より簡単な解決策がある。Pusher.このチュートリアルの目的には、このアプリケーションをお勧めしますが、APNを使用する他のお好みのサービスを使用できます。

SSL証明書の生成
Pusherアプリを使ってプッシュ通知を送信するには、SSL証明書を生成する必要があります。これを行うには、Apple Developerアカウントにログインし、アプリIDの証明書、ID、プロフィールページをご覧ください。をクリックして、プッシュ通知サービスを設定できます。編集ボタンをクリックします。以下のスクリーンショットのように、ウェブサイトの指示に従って証明書を作成します:

証明書が生成されダウンロードされたら、それをダブルクリックしてキーチェインに追加します。Pusherアプリの証明書ドロ ップダウンで選択できるようになります。
Google Firebase
macOS/iOS用に開発する場合は、前のセクションにジャンプしてください。Apple Developerをご覧ください。
このチュートリアルでは、Google Firebaseを使ってAndroidのリモート通知をテストする。まず、Firebaseコンソールで新しいプロジェクトを作成し、Androidアプリをプロジェクトに追加します。プロジェクトにはユニークなパッケージ名を選んでください。プロジェクトのクラウド・メッセージングタブをクリックすると、次の画面でサーバー送信者IDにアクセスできます:

の以下のコード・スニペットに、この送信者IDを挿入する。MainContentComponentビルダー
      #if JUCE_ANDROID
        remoteView.sendRemoteMessageButton.onClick = [this]
        {
            juce::StringPairArray data;
            data.set ("key1", "value1");
            data.set ("key2", "value2");
 
            static int id = 100;
            juce::PushNotifications::getInstance()->sendUpstreamMessage ("XXXXXXXXXXXX", // Insert sender ID here
                                                                         "com.juce.pushnotificationstutorial",
                                                                         juce::String (id++),
                                                                         "standardType",
                                                                         3600,
                                                                         data);
        };
Google FirebaseのバンドルIDが、プロジェクト設定のバンドルIDと一致していることを確認してください(すべて小文字)。
google-services.jsonファイルの生成
また、Androidのリモート通知用のFirebase設定ファイルをダウンロードする必要がある。設定ファイルを一般タブをクリックし、ダウンロードをクリックします。google-services.json次のスクリーンショットのように:

JUCEプロジェクト・ディレクトリにファイルをコピーし、Android exporter settingsのリモート通知設定ファイルフィールドにいる:

google-services.jsonへのパスは、"Debug "セクションと "Release "セクションで、ターゲットごとに個別に指定することもできます。これらはメインのAndroid exporterセクションの設定を上書きします。
プッシュ通知のセットアップはもう完了しているはずなので、ようやくアプリにこれらの機能を実装し始めることができる。
通知の種類
通知は、アプリが起動しているかどうかに関わらず、関連するコンテンツをユーザーに通知するのに便利です。単にメッセージを表示したり、ダイアログボックスを表示したり、音を鳴らしたりすることもできます。一般的に、すべての関連プラットフォームでは、主に2種類の通知があります:
- ローカル:システムによってローカルにトリガーされるため、インターネット接続を必要とせず、アプリがアクティブでないときでも起動するようにアプリによってスケジュールできる。
 - リモート:リモートサーバーから送信されるため、インターネット接続が必要だが、顧客のデバイスから受 信したデータに基づいていつでも送信できる。
 
まず、アプリ内で実行できる簡単なローカル通知を見てみよう。
リスナーとして登録する
についてPushNotificationsクラスはsingletonそのグローバル・インスタンスには、関数PushNotifications::getInstance().そのPushNotificationsクラスは、アクションの結果がPushNotifications::Listenerインタフェースにリスナーを登録するだけです。リスナーをPushNotificationsインスタンスがコールバックを受け取る。その結果MainContentComponentクラスを継承している。PushNotifications::Listener [1]:
class MainContentComponent   : public juce::Component,
                               private juce::ChangeListener,
                               private juce::ComponentListener,
                               private juce::PushNotifications::Listener // [1]
{
のコンストラクタではMainContentComponentクラスのリスナーとして追加します。PushNotificationsインスタンス[2]:
        juce::PushNotifications::getInstance()->addListener (this);     // [2]
また、次のようにクラスのデストラクタで登録を解除する。[3]:
    ~MainContentComponent() override
    {
        juce::PushNotifications::getInstance()->removeListener (this);  // [3]
    }
アクセス許可の申請
通知を送信する前に、まずユーザーから許可を得る必要があります。これはアプリが最初に起動されたときに一度だけ要求され、アプリが削除されるか、ユーザーがシステム設定で権限を取り消すまで、設定は保存されたままになります。そのため、アプリケーションの起動ごとに許可を要求し、許可が下りない場合にのみユーザーに許可を求めるのが良い習慣です。macOS/iOSではrequestPermissionsWithSettings()関数はPushNotifications::Settingsオブジェクトを引数[4].AndroidではsetupChannels()関数はPushNotifications::ChannelGroupオブジェクトとPushNotifications::Channelオブジェクトを引数に取る[5]。
      #if JUCE_IOS || JUCE_MAC
        paramControls.fireInComboBox.onChange = [this] { delayNotification(); };
        juce::PushNotifications::getInstance()->requestPermissionsWithSettings (getNotificationSettings()); // [4]
      #elif JUCE_ANDROID
        juce::PushNotifications::ChannelGroup cg { "demoGroup", "demo group" };
        juce::PushNotifications::getInstance()->setupChannels ({{ cg }}, getAndroidChannels());             // [5]
      #endif
    }
setupChannels()はAndroid Oreo(API 26)以降でのみ必要で、それ以前のAndroidバージョンでは無視されることに注意。
macOS/iOSでパーミッションを要求する
まずmacOS/iOS側を見てみよう。
要求するパーミッションには、アラート、バッジ、サウンドの3種類があります。この初期化を容易にするためにgetNotificationSettings()関数にこのコードを挿入する。関数のPushNotifications::Settingsオブジェクトに対応する変数を設定し、以下の機能を許可する。[6].macOSでデプロイする場合は、これだけリクエストすれば関数から戻ることができる。しかしiOSでは、以下のように定義されたActionオブジェクトとCategoryオブジェクトを定義しなければならない:
- アクション:通知へのボタンまたはテキスト入力として提示できるアクションを表します。
 - カテゴリー:通知で一緒に表示される一連のアクションを表します。
 
同じ関数で、アクションとカテゴリーを以下のように定義する:
    static juce::PushNotifications::Settings getNotificationSettings()
    {
        juce::PushNotifications::Settings settings;                             // [6]
        settings.allowAlert = true;
        settings.allowBadge = true;
        settings.allowSound = true;
 
      #if JUCE_IOS
        using Action   = juce::PushNotifications::Settings::Action;
        using Category = juce::PushNotifications::Settings::Category;
 
        Action okAction;
        okAction.identifier = "okAction";
        okAction.title = "OK!";
        okAction.style = Action::button;
        okAction.triggerInBackground = true;
 
        Action cancelAction;
        cancelAction.identifier = "cancelAction";
        cancelAction.title = "Cancel";
        cancelAction.style = Action::button;
        cancelAction.triggerInBackground = true;
        cancelAction.destructive = true;
 
        Action textAction;
        textAction.identifier = "textAction";
        textAction.title = "Enter text";
        textAction.style = Action::text;
        textAction.triggerInBackground = true;
        textAction.destructive = false;
        textAction.textInputButtonText = "Ok";
        textAction.textInputPlaceholder = "Enter text...";
 
        Category okCategory;
        okCategory.identifier = "okCategory";
        okCategory.actions = { okAction };
 
        Category okCancelCategory;
        okCancelCategory.identifier = "okCancelCategory";
        okCancelCategory.actions = { okAction, cancelAction };
 
        Category textCategory;
        textCategory.identifier = "textCategory";
        textCategory.actions = { textAction };
        textCategory.sendDismissAction = true;
 
        settings.categories = { okCategory, okCancelCategory, textCategory };   // [7]
      #endif
 
        return settings;
    }
ここでは、3つの異なるアクションと3つの異なるカテゴリーを作成した:
- OKアクション:背景 でトリガーされる適切な "OK "タイトルを持つボタンとしてのアクションスタイルを指定します。
 - キャンセルアクション:背景でトリガーされ、破壊的に表示される適切な "Cancel "タイトルを持つボタンとしてアクションスタイルを指定します。
 - テキストアクション:背景でトリガーされる適切なプレースホルダーとボタンテキストを持つテキスト入力としてアクションスタイルを指定します。
 - OKカテゴリー:OKアクション」のみを表す。
 - OK/キャンセルカテゴリー:OKアクション」と「キャンセルアクション」を表す。
 - テキストカテゴリー:テキスト・アクション」のみを表し、却下アクションが送信されることを指定する。
 
これらのカテゴリーはすべてPushNotifications::Settingsオブジェクト[7]。
Androidでパーミッションを要求する
では、アンドロイド側を見てみよう。
対応する設定で定義できる重要度の異なるチャンネルがあり、これらのチャンネルをチャンネル・グループの一部にして、通知を視覚的に分けることができる。この初期化を容易にするためにgetAndroidChannels()関数にこのコードを挿入する。3つの異なるPushNotifications::Channelオブジェクトで異なるパラメータを指定する[8]:
    static juce::Array getAndroidChannels()
    {
        using Channel = juce::PushNotifications::Channel;
 
        Channel ch1, ch2, ch3;
 
        ch1.identifier = "1";
        ch1.name = "HighImportance";
        ch1.importance = juce::PushNotifications::Channel::max;
        ch1.lockScreenAppearance = juce::PushNotifications::Notification::showCompletely;
        ch1.description = "High Priority Channel for important stuff";
        ch1.groupId = "demoGroup";
        ch1.ledColour = juce::Colours::red;
        ch1.bypassDoNotDisturb = true;
        ch1.canShowBadge = true;
        ch1.enableLights = true;
        ch1.enableVibration = true;
        ch1.soundToPlay = juce::URL ("demonstrative");
        ch1.vibrationPattern = { 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200 };
 
        ch2.identifier = "2";
        ch2.name = "MediumImportance";
        ch2.importance = juce::PushNotifications::Channel::normal;
        ch2.lockScreenAppearance = juce::PushNotifications::Notification::showPartially;
        ch2.description = "Medium Priority Channel for standard stuff";
        ch2.groupId = "demoGroup";
        ch2.ledColour = juce::Colours::yellow;
        ch2.canShowBadge = true;
        ch2.enableLights = true;
        ch2.enableVibration = true;
        ch2.soundToPlay = juce::URL ("default_os_sound");
        ch2.vibrationPattern = { 1000, 1000 };
 
        ch3.identifier = "3";
        ch3.name = "LowImportance";
        ch3.importance = juce::PushNotifications::Channel::min;
        ch3.lockScreenAppearance = juce::PushNotifications::Notification::dontShow;
        ch3.description = "Low Priority Channel for silly stuff";
        ch3.groupId = "demoGroup";
 
        return { ch1, ch2, ch3 };
    }
- チャンネル1:最も重要な通知。赤にする色、"Do Not Disturb "のバイパス、カスタム振動パターンの起動、カスタムサウンドファイルの再生などのパラメーターを指定できます。
 - チャンネル2:通知における中程度の重要度を表します。今回は、黄色、異なるバイブレーションパターン、異なるカスタムサウンドファイルを指定します。
 - チャンネル3:通知の中で最も重要度の低いものを表す。これらのパラメータはデフォルトのままとする。
 
の配列として返される。PushNotifications::Channel同じPushNotifications::ChannelGroup [9].
getAndroidChannels()の実装は、チャンネルごとに少なくとも識別子、名前、グループIDを提供することで、チャンネルを正しく設定しなければならない。
これらのプラットフォームでの通知許可に慣れるために、これらの設定を自由に試して変更してください。
すべてのプラットフォームで通知のセットアップが完了しました。
ローカル通知
ユ  ーザーがローカル通知タブの送信ボタンをクリックすると、以下のようになる。sendLocalNotification()関数が呼び出される。この関数はまずPushNotifications::Notificationオブジェクトを作成し、そのオブジェクトにユーザーインタフェースの入力から対応するパラメータを入力する。[1].通知オブジェクトが不正なパラメータのために無効である場合、このチュートリアルの目的のために、オプションでメッセージを表示します。[2].そうでなければ、オブジェクトが有効であればsendLocalNotification()関数を使用する。PushNotificationsインスタンス[3]。
    void sendLocalNotification()
    {
        juce::PushNotifications::Notification n;                            // [1]
 
        fillRequiredParams (n);                                             // [4]
        fillOptionalParamsOne (n);
      #if JUCE_ANDROID
        fillOptionalParamsTwo (n);
        fillOptionalParamsThree (n);
      #endif
 
        if (! n.isValid())                                                  // [2]
        {
          #if JUCE_IOS
            juce::String requiredFields = "identifier (from iOS 10), title, body and category";
          #elif JUCE_ANDROID
            juce::String requiredFields = "channel ID (from Android O), title, body and icon";
          #else
            juce::String requiredFields = "all required fields";
          #endif
 
            juce::NativeMessageBox::showMessageBoxAsync (juce::AlertWindow::InfoIcon,
                                                         "Incorrect notifications setup",
                                                         "Please make sure that "
                                                         + requiredFields + " are set.");
 
 
            return;
        }
 
        juce::PushNotifications::getInstance()->sendLocalNotification (n);  // [3]
    }
今のところ、必要なパラメータに焦点を当てる。fillRequiredParams()関数に渡す。PushNotifications::Notificationオブジェクトを引数[4]。
プッシュ通知のオプション・パラメーターに興味がある方は、このチュートリアルの最後のセクションを参照してください。Customising notificationsそこで、このことについて詳しく説明する。
    void fillRequiredParams      (juce::PushNotifications::Notification& n)
    {
        n.identifier = paramControls.identifierEditor.getText();            // [5.1]
        n.title = paramControls.titleEditor.getText();                      // [5.2]
        n.body = paramControls.bodyEditor.getText();                        // [5.3]
      #if JUCE_IOS
        n.category = paramControls.categoryComboBox.getText();              // [6.1]
      #elif JUCE_ANDROID || JUCE_MAC
       #if JUCE_MAC
        juce::String prefix = "images/";
        juce::String extension = ".png";
       #else
        juce::String prefix;
        juce::String extension;
       #endif
        if (paramControls.iconComboBox.getSelectedItemIndex() == 0)         // [7]
            n.icon = prefix + "ic_stat_name" + extension;
        else if (paramControls.iconComboBox.getSelectedItemIndex() == 1)
            n.icon = prefix + "ic_stat_name2" + extension;
        else if (paramControls.iconComboBox.getSelectedItemIndex() == 2)
            n.icon = prefix + "ic_stat_name3" + extension;
        else if (paramControls.iconComboBox.getSelectedItemIndex() == 3)
            n.icon = prefix + "ic_stat_name4" + extension;
        else if (paramControls.iconComboBox.getSelectedItemIndex() == 4)
            n.icon = prefix + "ic_stat_name5" + extension;
      #endif
 
      #if JUCE_ANDROID
        // Note: this is not strictly speaking required param, just doing it here because it is the fastest way!
        n.publicVersion = new juce::PushNotifications::Notification();      // [8]
        n.publicVersion->identifier = "blahblahblah";
        n.publicVersion->title = "Public title!";
        n.publicVersion->body  = "Public body!";
        n.publicVersion->icon  = n.icon;
 
        n.channelId = String (paramControls.channelIdComboBox.getSelectedItemIndex() + 1); // [6.2]
      #endif
    }
すべてのプラットフォームにおいて、通知は3つの主要パラメータで定義され、必須パラメータタブのユーザー入力から設定します:
- 特定する[5.1]内部で通知を識別するためのユニークなID。通常、ユーザーには表示されません。
 - タイトル[5.2]バブルの上部に表示される通知のタイトル。
 - ボディ[5.3]: トリミングすると全体が表示されるように展開できる通知の内容。
 
macOS/iOSでは、表示するアクションを選択するための通知カテゴリも提供しています。[6.1].Androidでは、チャネル番号を指定して通知の重要度を指定します。[6.2]。
macOSとAndroidでは、通知内に表示するアイコンを提供する必要があり、パスがプラットフォーム固有であることを確認します。[7].最後にアンドロイドでは、必要に応じて、デバイスのロック画面に表示される通知の別バージョンをできますます。[8]。
ローカル通知の実装はかなり簡単なので、次はリモート通知に飛び込もう。
リモート通知
このセクションは物理的なデバイス上でのみ機能します。シミュレーターでは正しく機能しませんので、試さないでください。
macOS/iOSでリモート通知を受け取るには、まず、テスト通知を送信するためにPusherアプリケーションに挿入するデバイストークンを取得する必要があります。このトークンはいつでも変更される可能性があるため、アプリケーションを起動するたびに新しいトークンを取得することをお勧めします。これはボタンのラムダ関数の中でgetDeviceToken()関数を使用する。PushNotificationsインスタンス[1]:
        remoteView.getDeviceTokenButton.onClick = []
        {
            juce::String token = juce::PushNotifications::getInstance()->getDeviceToken();
 
            DBG ("token = " + token);
 
            if (token.isEmpty())
                showRemoteInstructions();
            else
                juce::NativeMessageBox::showMessageBoxAsync (juce::AlertWindow::InfoIcon, "Device token", token);
        };
トークンがリフレッシュされるたびに、コールバック関数deviceTokenRefreshed()がトリガーされ、この変更が通知される:
    void deviceTokenRefreshed (const juce::String& token) override
    {
        juce::NativeMessageBox::showMessageBoxAsync (juce::AlertWindow::InfoIcon,
                                                     "Device token refreshed",
                                                     token);
    }
macOS/iOSでのリモート通知
Pusherに飛び、macOS/iOS上のアプリにプッシュ通知を送る方法を見てみよう。デバッガーから新しいデバイストークンをコピーし、Pusherアプリを起動します。トークンを適切なデバイス・プッシュ・トークンフィールドで、正しい証明書がドロップダウンメニューで選択されていることを確認してください。
下のテキスト・フィールドで、ペイロード辞書としてプッシュ通知を作成するにはJSON形式を使用します。単純な通知は次のようになる:
{
    "aps" :
    {
        "alert" : "Test Push Notification",
        "badge" : 1,
        "sound" : "default"
    },
    "juce" : "tutorial",
    "foo" : "bar"
}
aps "と呼ばれる最初の要素には、通知に必要な情報が含まれます。上記の例では、アラート、バッジカウント、トリガー時に再生するサウンドを記述しています。また、"aps "要素に続くkey/valueペアの例で示したように、アプリケーション固有のデータを送信することもできます。
この2つ目のペイロードの例では、タイトルフィールドに加えて、本文やアクションボタンなど、より多くの情報を含むようにアラートをカスタマイズします。また、通知がトリガーされた時にカスタムサウンドファイルを再生することにしました:
{
    "aps" :
    {
        "alert" :
        {
            "title" : "Test Push Notification",
            "body" : "Hello World!",
            "action-loc-key" : "OK"
        },
        "badge" : 2,
        "sound" : "demonstrative.caf"
    }
}
3つ目の例では、次のように定義された通知カテゴリーを使用します。PushNotifications::Settingsアクションを簡単に定義できます。また、通知をバックグラウンドで静かに配信するようOSに指示します。content-available財産である:
{
    "aps" :
    {
        "category" : "okCategory",
        "alert" : "Test Push Notification",
        "badge" : 3,
        "content-available" : 1
    },
    "foobar" : [ "foo", "bar" ]
}
様々なペイロードパラメータを試し、それらがアプリ内でどのように受け取られるかを見てみましょう。通知本文に画像を表示できますか?
アンドロイドでのリモート通知
ここでAndroid側に切り替えて、Google Firebaseコンソールを使ってみよう。コンソールで成長 > 通知タブをクリックしメッセージを作成する.次のウィンドウで、メッセージの情報を入力し、通知を送信するターゲットを選択します。
Androidでは、アプリからサーバーにアップストリームでメッセージを送信し、重要なユーザーデータを伝達することもできる。これは、キーと値のペアの辞書を