web-dev-qa-db-ja.com

Android-ローカルの通知を表示するためにAlarmManagerがBroadcastReceiverを起動しない

私はアプリケーションがPrayerTimesでローカル通知を表示することを要求する祈りのアプリケーションを書いています。祈りの時間は毎日異なります。したがって、次のコードを使用して、BroadcastReceiverからのロケーション通知と、そのスケジュールの次の通知の直後を表示しています。

問題は、通知が特定のタイミングで起動し続けるために、アプリケーションが少なくとも1日に1回開く必要があることです。

アプリを開かずにローカル通知を起動するようにアラームマネージャーを使用してBroadcastReceiverをスケジュールする方法はありますか?

fun MakkahPrayer.setNotificationForPrayer(prayer: Prayer, date: Date) {
    val app = App.instance!!.applicationContext
    val preferences = PreferenceManager.getInstance(app)

    if(!preferences.isPrayerAlarmSet(prayer.name)) {
        val calendar = Calendar.getInstance()
        calendar.add(Calendar.DAY_OF_YEAR, 0)

        val dayOfYear = calendar[Calendar.DAY_OF_YEAR]

        NotificationUtils.instance.setNotification(date.time, prayer.name, dayOfYear.toString())
        preferences.setPrayerIsAlarmOn(prayer.name, true)
    }
}

NotificationUtils.kt

class NotificationUtils {
    companion object {
        val instance = NotificationUtils()
    }

    fun setNotification(timeInMilliSeconds: Long, name: String, day: String) {

        val cal = Calendar.getInstance()
        cal.time = Date()
        val millis = cal.timeInMillis

        if (timeInMilliSeconds > 0 && timeInMilliSeconds > millis) {

            val key = name + day

            val alarmManager =
                App.instance?.getSystemService(Activity.ALARM_SERVICE) as AlarmManager
            val alarmIntent = Intent(App.instance?.applicationContext, AlarmReceiver::class.Java)

            alarmIntent.putExtra("prayer", name)
            alarmIntent.putExtra("timestamp", timeInMilliSeconds)
            alarmIntent.putExtra("notificationID", key)

            val calendar = Calendar.getInstance()
            calendar.timeInMillis = timeInMilliSeconds

            val pendingIntent = PendingIntent.getBroadcast(
                App.instance,
                timeInMilliSeconds.toInt(),
                alarmIntent,
                PendingIntent.FLAG_UPDATE_CURRENT
            )

            alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeInMilliSeconds, pendingIntent)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMilliSeconds, pendingIntent)
            } else {
                alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeInMilliSeconds, pendingIntent)
            }
        }
    }
}

AlarmReceiver.kt

class AlarmReceiver : BroadcastReceiver() {
    companion object {
        private lateinit var mNotification: Notification

        const val CHANNEL_ID = "CHANNEL_ID"
        const val CHANNEL_NAME = "Prayer Notification"
    }

    override fun onReceive(context: Context, intent: Intent) {
        val manager = createChannel(context)
        showNotification(context, intent, manager)
        setNextPrayerAlarm(intent)
    }

    private fun setNextPrayerAlarm(intent: Intent) {
        if (intent.extras != null) {
            val prayerName = intent.extras!!.getString("prayer", "Prayer")
            val prayer = Prayer.valueOf(prayerName)
            MakkahPrayer.instance.removePrayerNotification(prayer)
        }

        val (nextPrayer, date) = MakkahPrayer.instance.nextPrayerWithTime()
        MakkahPrayer.instance.setNotificationForPrayer(nextPrayer, date)
    }

    private fun showNotification(
        context: Context,
        intent: Intent,
        notificationManager: NotificationManager
    ) {
        var timestamp: Long = 0
        var prayerName = "Prayer"

        var mNotificationId = ""

        if (intent.extras != null) {
            timestamp = intent.extras!!.getLong("timestamp")
            prayerName = intent.extras!!.getString("prayer", "Prayer")
            mNotificationId = intent.extras!!.getString("notificationID", "")
        }

        if (timestamp > 0) {
            val notifyIntent = Intent(context, MainActivity::class.Java)

            val title = capitalize(prayerName)
            val message = "It is $title time"

            notifyIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK

            val calendar = Calendar.getInstance()
            calendar.timeInMillis = timestamp

            val pendingIntent = PendingIntent.getActivity(
                context,
                0,
                notifyIntent,
                PendingIntent.FLAG_UPDATE_CURRENT
            )
            val uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)

            mNotification = NotificationCompat.Builder(context, NotificationService.CHANNEL_ID)
                .setContentIntent(pendingIntent)
                .setSmallIcon(R.drawable.ic_alarm_black_24dp)
                .setLargeIcon(
                    BitmapFactory.decodeResource(
                        context.resources,
                        R.mipmap.ic_launcher
                    )
                )
                .setSound(uri)
                .setAutoCancel(true)
                .setContentTitle(title)
                .setStyle(
                    NotificationCompat.BigTextStyle()
                        .bigText(message)
                )
                .setColor(ContextCompat.getColor(context, R.color.colorSecondary))
                .setContentText(message).build()

            notificationManager.notify(timestamp.toInt(), mNotification)
        }
    }

    @SuppressLint("NewApi")
    private fun createChannel(context: Context): NotificationManager {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val soundUri =
                Uri.parse(ContentResolver.SCHEME_Android_RESOURCE + "://" + App.instance?.applicationContext?.packageName + "/" + R.raw.azan)

            val audioAttributes = AudioAttributes.Builder()
                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                .setUsage(AudioAttributes.USAGE_NOTIFICATION)
                .build()

            val notificationManager =
                context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

            val importance = NotificationManager.IMPORTANCE_HIGH
            val channel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME, importance)
            channel.enableVibration(true)
            channel.setShowBadge(true)
            channel.canShowBadge()
            channel.enableLights(true)
            channel.lightColor = context.getColor(R.color.colorSecondary)
            channel.description =
                context.getString(R.string.notification_channel_description)
            channel.setSound(soundUri, audioAttributes)
            channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
            notificationManager.createNotificationChannel(channel)

            return notificationManager
        } else {
            return context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        }
    }
}

編集:以下の方法を使用した後、以下の人々が説明しているように、それはまだ機能していません。つまり、アプリがローカル通知を生成するには、24時間に少なくとも1回開いている必要があります。私はソリューションを探しています。ここでは、アプリを少なくとも4,5日間開いておく必要はなく、アプリはローカル通知を配信する必要があります。現時点では24時間しか機能しません。翌日になると通知が停止し、少なくとも1日に1回はアプリを開く必要があります。

4
Muhammad Umer

Androidxを使用してPrayerWorkerを作成できますWork ManagerバックグラウンドAPI /通知の設定をスケジュールします(すべて、アプリを開くことなく、代わりに通知を受信したときにトリガーされます)。

ドキュメントは次の場所にあります ここ

代わりに、setNextPrayerAlarm関数はロジックをPrayerWorkerに移動し、次のようになります。

private fun setNextPrayerAlarm(intent: Intent) {
    if (intent.extras != null) {
        val oneTimeWorkRequestBuilder = OneTimeWorkRequest.Builder(PrayerWorker::class.Java)
        oneTimeWorkRequestBuilder.setInputData(`put your input data here`)
        WorkManager.getInstance(context).enqueueUniqueWork("setPrayerWorker",ExistingWorkPolicy.REPLACE, oneTimeWorkRequestBuilder.build())
    }            
}

PrayerWorkerは次のようになります

class PrayerWorker(context: Context, workerParameters: WorkerParameters): Worker(context, workerParameters) {
    override fun doWork(): Result {
        //Insert logic to determine alarms to set 
        return Result.success() //for success case
    }
}

編集1:

こんにちは、私は方法でより明確であったはずです、申し訳ありません。これを繰り返しアラームにする方法は2つあります。

方法1:OneTimeWorkRequestPeriodicWorkRequestに変更します(ドキュメントを参照 here )。この方法を使用すると、ワーカーが繰り返す方法を指定できます(たとえば、2時間ごと、24時間ごと)。最小間隔は15分です。

方法2:PrayerWorkerを変更して、次のワーカーもスケジュールするようにします。これは、ワーカーのトリガーに遅延を追加できるという事実を利用します(ドキュメントを参照)。この場合は24時間です。以下は例です

    class PrayerWorker(context: Context, workerParameters: WorkerParameters): Worker(context, workerParameters) {
            override fun doWork(): Result {
                //Insert logic to determine alarms to set 
                val oneTimeWorkRequestBuilder = OneTimeWorkRequest.Builder(PrayerWorker::class.Java)
                oneTimeWorkRequestBuilder.setInputData(`put your input data here`)
oneTimeWorkRequestBuilder.setInitialDelay(`initialDelay`, `timeUnit`)
              WorkManager.getInstance(context).enqueueUniqueWork("setPrayerWorker",ExistingWorkPolicy.REPLACE, oneTimeWorkRequestBuilder.build())
                return Result.success() //for success case
            }
    }

次の手順をお試しください

1。NotificationUtils.ktインテントフラグを追加FLAG_RECEIVER_FOREGROUND

以下のようにあなたのためのトリックを行います

        val alarmIntent = Intent(App.instance?.applicationContext, AlarmReceiver::class.Java)
        alarmIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        alarmIntent.putExtra("prayer", name)
        ....
        ...

2。また、AlarmReceiverがマニフェストに登録されていることを確認してください

以下のように

<receiver Android:name="com.myapp.receiver.AlarmReceiver">
    </receiver>
0

どのAndroid=アプリが対象とするSDKレベル)かわかりませんが、GoogleはAPIsをOから変更しています。マニフェストから暗黙のブロードキャストレシーバーを宣言すると、うまくいかない。

As part of the Android 8.0 (API level 26) Background Execution Limits, apps that target the API level 26 or higher can no longer register broadcast receivers for implicit broadcasts in their manifest. However, several broadcasts are currently exempted from these limitations. Apps can continue to register listeners for the following broadcasts, no matter what API level the apps target.

詳細はこちら: https://developer.Android.com/guide/components/broadcast-exceptions

0
AouledIssa