Unity/Native連携/Kotlinバックグランド通知
提供: 初心者エンジニアの簡易メモ
通知欄を見に行くと、追加される。トップ画面へのpopupはされない。(IMPORTANCE_HIGHや、NotificationCompat.PRIORITY_HIGHを追加でpopupされると合ったが何故かされない・・・)
Unity側
RenkeiScene.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
public class RenkeiScene : MonoBehaviour
{
[SerializeField] Button alarmButton;
void Start()
{
alarmButton.onClick.AddListener(CallMethodAlarm);
}
void CallMethodAlarm()
{
SetAlarm(10);
}
#if UNITY_ANDROID && !UNITY_EDITOR
public void SetAlarm(int second)
{
Debug.Log("SetAlarm second=" + second);
using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject context = activity.Call<AndroidJavaObject>("getApplicationContext");
AndroidJavaClass alarmReceiver = new AndroidJavaClass("com.example.mylibrary.AlarmReceiver");
alarmReceiver.CallStatic("setAlarm", context, second);
}
}
// 権限付与
public void RequestExactAlarmPermission()
{
using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject alarmPermissionHelper = new AndroidJavaObject("com.example.alarmapp.AlarmPermissionHelper");
alarmPermissionHelper.Call("requestExactAlarmPermission", activity);
}
}
#endif
}
Android側
AlarmPermissionHelper.kt
package com.example.mylibrary
import android.content.Intent
import android.provider.Settings
import android.app.AlarmManager
import android.content.Context
import android.os.Build
import android.net.Uri
class AlarmPermissionHelper {
fun requestExactAlarmPermission(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
if (!alarmManager.canScheduleExactAlarms()) {
// アラームの許可がない場合、設定画面を開く
val intent = Intent().apply {
action = Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM
data = Uri.parse("package:${context.packageName}")
}
context.startActivity(intent)
}
}
}
}
AlarmReceiver.kt
package com.example.mylibrary
import android.app.AlarmManager
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.SystemClock
import android.util.Log
import java.util.Calendar
import android.os.Build
import androidx.core.app.NotificationCompat
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.i("AlarmReceiver", "Notification triggered")
// 通知を作成して表示
val notificationId = 1
val channelId = "alarm_channel"
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelName = "Alarm Channel"
val channelDescription = "Channel for alarm notifications"
val importance = NotificationManager.IMPORTANCE_HIGH
val notificationChannel = NotificationChannel(channelId, channelName, importance).apply {
description = channelDescription
}
notificationManager.createNotificationChannel(notificationChannel)
Log.i("AlarmReceiver", "Notification Channel Created")
// API 26+ の場合
val notification = NotificationCompat.Builder(context, channelId)
.setSmallIcon(android.R.drawable.ic_notification_overlay) // 通知アイコン
.setContentTitle("アラーム通知") // 通知タイトル
.setContentText("指定した時間が経過しました!") // 通知内容
.setPriority(NotificationCompat.PRIORITY_HIGH)
.build()
notificationManager.notify(notificationId, notification)
Log.i("AlarmReceiver", "Notification Posted")
} else {
// API 26 未満の場合
val notification = NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_notification) // 通知アイコン
.setContentTitle("アラーム通知") // 通知タイトル
.setContentText("指定した時間が経過しました!") // 通知内容
.setPriority(NotificationCompat.PRIORITY_HIGH)
.build()
notificationManager.notify(notificationId, notification)
Log.i("AlarmReceiver", "Notification Posted")
}
}
companion object {
@JvmStatic
fun setAlarm(context: Context, second: Int) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(context, AlarmReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
val triggerAtMillis = SystemClock.elapsedRealtime() + calculateTriggerTime(second)
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, pendingIntent)
}
private fun calculateTriggerTime(second: Int): Long {
// 現在の時間を取得
val now = System.currentTimeMillis()
val calendar = Calendar.getInstance()
calendar.timeInMillis = now
// 現在の時間に指定された秒を加算
calendar.add(Calendar.SECOND, second)
// 計算された時間が現在の時間より後であることを確認
return calendar.timeInMillis - now
}
}
}
Assets/Plugins/Android/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.player"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<!-- アラーム受信に必要な権限(通常は不要) -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- 通知権限 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application>
<receiver
android:name="com.example.mylibrary.AlarmReceiver"
android:enabled="true"
android:exported="true" />
<activity android:name="com.unity3d.player.UnityPlayerActivity"
android:theme="@style/UnityThemeSelector">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
</application>
</manifest>
Assets/Plugins/Android/mainTemplate.gradle
dependencies {
+ implementation 'androidx.core:core:1.12.0'
}
NotificationCompatを使うためandroidxが必要。
Assets/Plugins/Android/gradleTemplate.properties
android.useAndroidX=true android.enableJetifier=true
