Zenn
🔔

【UE5】Androidの通知許可をリクエストする

2025/02/27に公開

はじめに

Android13からプッシュ通知をするためにはユーザーから権限を許可してもらう必要があります。
私のAndroidは12なので、アプリの通知はデフォルトで許可する設定になっているためそんなことは露知らず・・・
Android SDK Versionを確認し、33以上であればAndroid13以上だという判断が下せるので、その場合にRuntime Permissionという機能で通知の許可をリクエストするようにしたらよいです。

・・・が、Android12以下に対して通知の権限を要求しても通知の権限が存在しないので、存在しない権限の要求は無視されます。
なので、面倒であれば全てのAndroidに対して通知の許可リクエストを行っても問題ないかなーと思いますが、しっかり作るのであればAndroid13以上に対してだけリクエストするべきですね。

開発環境

  • Windows 11
  • UE5.3.2
  • Visual Studio 2022
  • Android Studio Flamingo 2022.2.1 Patch 2
  • Xiaomi 11T(Android13)
  • Xperia 1 Ⅱ(Android12)

事前知識

JNIについて

通知許可のリクエストをするカスタムノードを作成するために、Java Native Interface(JNI)を使います。
JNIとは、Javaと他言語(CやC++)が連携するためのインターフェースのことで、Javaから他言語のメソッドを呼び出せたり、他言語からJavaのメソッドを呼び出せたりします。

通知チャンネルについて

Androidには通知チャンネルという機能があり、ユーザーが欲しい通知だけを取得するための機能です。
下の画像のようなやつです。

私のXの通知チャンネルですが、DMが来たら通知を出すようにしていますが、ニュースの通知は受け取らないようにしています。
このように、通知の種類ごとにオンオフを切り替えることができる機能のことです。

プッシュ通知実装の準備に必要なノードについて

プッシュ通知を実装するための準備に必要なノードは最低でも1つ、細かく作るなら4つ必要です。
今回は通知許可リクエストだけの解説になります。

  • 通知許可ダイアログを表示させるノード(必須)
  • Android SDK Versionを返すノード
  • 通知チャンネルを作成するノード
  • 通知を許可したか拒否したかを取得するノード

処理の流れ的には、

  1. デバイスがAndroidなら通知チャンネルを作成
  2. Android SDK Versionを取得
  3. 33以上なら通知許可ダイアログを表示、そうでないなら次へ
  4. 通知を許可したか拒否したかを取得し、通知を送信する

最後の通知を送信するところはFirebase Cloud Messaging(FCM)などを使って送信すると良いと思います。
FCMでは通知を送信する際に通知チャンネルを指定できるので、作成した通知チャンネルを指定して送信すると良いです。(画像の場合だと、「アップデート通知」という通知チャンネルに通知を送信する)

Firebase Cloud Messagingの通知編集画面

ちなみに通知チャンネルを作成せずにFCMでプッシュ通知を送信すると、「miscellaneous」という通知チャンネルが自動で作成され、通知チャンネルを指定しない通知とテスト通知はこのチャンネルで送られてきます。

UEの標準ノードにも通知を送信できそうな名前のノードがあったような気がしましたが、使ったことがないのでわかりません。

実装

新規C++クラスを作成します。
親クラスはBlueprintAsyncActionBaseクラスを選択します。

クラス名はお好きに。
今回はRequestNotificationPermissionにしました。

コードの実装です。

RequestNotificationPermission.h
#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "RequestNotificationPermission.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FRequestNotificationPermissionCallback);

UCLASS()
class NOTIFICATIONTEST_API URequestNotificationPermission : public UBlueprintAsyncActionBase
{
	GENERATED_BODY()

public:
	UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject"))
	static URequestNotificationPermission* RequestNotificationPermission(UObject* WorldContextObject);

	UPROPERTY(BlueprintAssignable)
	FRequestNotificationPermissionCallback Completed;

	void Activate() override;
};
RequestNotificationPermission.cpp
#include "RequestNotificationPermission.h"

#if PLATFORM_ANDROID
#include "Android/AndroidApplication.h"
#endif

URequestNotificationPermission* URequestNotificationPermission::RequestNotificationPermission(UObject* WorldContextObject)
{
    URequestNotificationPermission* Instance = NewObject<URequestNotificationPermission>();
    Instance->RegisterWithGameInstance(WorldContextObject);
    return Instance;
}

void URequestNotificationPermission::Activate()
{
#if PLATFORM_ANDROID
    JNIEnv* Env = FAndroidApplication::GetJavaEnv();

    if (Env != nullptr)
    {
        // Activityを取得
        jobject Activity = FAndroidApplication::GetGameActivityThis();

        // requestPermissionsメソッドを取得
        jmethodID RequestPermissionsMethod = Env->GetMethodID(Env->GetObjectClass(Activity), "requestPermissions", "([Ljava/lang/String;I)V");

        // 権限リストを作成
        jclass StringClass = Env->FindClass("java/lang/String");
        jobjectArray PermissionArray = Env->NewObjectArray(1, StringClass, nullptr);
        jstring Permission = Env->NewStringUTF("android.permission.POST_NOTIFICATIONS");
        Env->SetObjectArrayElement(PermissionArray, 0, Permission);

        // 権限をリクエスト
        Env->CallVoidMethod(Activity, RequestPermissionsMethod, PermissionArray, 1);

        // ローカル参照を削除
        Env->DeleteLocalRef(StringClass);
        Env->DeleteLocalRef(PermissionArray);
        Env->DeleteLocalRef(Permission);
    }

    AsyncTask(ENamedThreads::GameThread, [this]() {
        Completed.Broadcast();
        SetReadyToDestroy();
    });
    return;
#else
    AsyncTask(ENamedThreads::GameThread, [this]() {
        Completed.Broadcast();
        SetReadyToDestroy();
    });
    return;
#endif
}

VSでビルドしてからEditorを起動し、レベルブループリントにノードを配置します。

実機確認

Android12

起動したら何も起こらずにCompletedが出力されました。

Android13

起動したら通知許可ダイアログが表示され、そのあとにCompletedが出力されました。

通知チャンネルを確認すると、unreal-afs-notification-channelという名前になっていました。
これがデフォルトらしいです。

まとめ

AndroidのOS機能を使うにはJNIを使いましたが、iOSではObjective-C++というObjective-CとC++が混じったカオス言語で記述することになります。
そちらも気が向いたら記事にします。

参考文献

https://qiita.com/shiena/items/7b9d0de97a733b1a5101

Discussion

ログインするとコメントできます