步骤
创建通知
NotificationUtils、EggTimerViewModel、EggTimerFragment
基本通知
-
在 NotificationManager 中实现一个扩展 fun sendN - 无需重写代码即可发送 Ns
- 创建一个扩展乐趣来发送消息
- 为通知创建内容意图以启动此活动
- 创建 PendingIntent
- 设置内容意图
- 取消所有通知
- 获取 NotificationCompat.Builder 的实例以构建通知
- 为 Builder 设置标题、文本和图标
- 呼叫通知
- 使用新的通知渠道
- 添加样式
- 向 Builder 添加样式
- 添加贪睡动作
- 设置优先级
// NotificationUtils.kt
package com.example.android.eggtimernotifications.util
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import androidx.core.app.NotificationCompat
import com.example.android.eggtimernotifications.MainActivity
import com.example.android.eggtimernotifications.R
import com.example.android.eggtimernotifications.receiver.SnoozeReceiver
// Notification ID.
private val NOTIFICATION_ID = 0
private val REQUEST_CODE = 0
private val FLAGS = 0
// Step 1.1 extension function to send messages (GIVEN)
/**
* Builds and delivers the notification.
*
* @param context, activity context.
*/
fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
// Create the content intent for the notification, which launches
// this activity
// TODO: Step 1.11 create intent
// TODO: Step 1.12 create PendingIntent
// TODO: Step 2.0 add style
// TODO: Step 2.2 add snooze action
// TODO: Step 1.2 get an instance of NotificationCompat.Builder
// Build the notification
val builder = NotificationCompat.Builder(
applicationContext,
applicationContext.getString(R.string.egg_notification_channel_id)
)
// TODO: Step 1.8 use the new 'breakfast' notification channel
// TODO: Step 1.3 set title, text and icon to builder
.setSmallIcon(R.drawable.cooked_egg)
.setContentTitle(applicationContext
.getString(R.string.notification_title))
.setContentText(messageBody)
// TODO: Step 1.13 set content intent
// TODO: Step 2.1 add style to builder
// TODO: Step 2.3 add snooze action
// TODO: Step 2.5 set priority
// TODO: Step 1.4 call notify
notify(NOTIFICATION_ID, builder.build())
}
// TODO: Step 1.14 Cancel all notifications
- 获取 NotificationManager 的实例并调用sendNotification和EggTimerViewModel
package com.example.android.eggtimernotifications.ui
import android.app.*
import android.content.Context
import android.content.Intent
import android.os.CountDownTimer
import android.os.SystemClock
import androidx.core.app.AlarmManagerCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.*
import com.example.android.eggtimernotifications.receiver.AlarmReceiver
import com.example.android.eggtimernotifications.R
import com.example.android.eggtimernotifications.util.sendNotification
import kotlinx.coroutines.*
class EggTimerViewModel(private val app: Application) : AndroidViewModel(app) {
private val REQUEST_CODE = 0
private val TRIGGER_TIME = "TRIGGER_AT"
private val minute: Long = 60_000L
private val second: Long = 1_000L
private val timerLengthOptions: IntArray
private val notifyPendingIntent: PendingIntent
private val alarmManager = app.getSystemService(Context.ALARM_SERVICE) as AlarmManager
private var prefs = app.getSharedPreferences(
"com.example.android.eggtimernotifications",
Context.MODE_PRIVATE
)
private val notifyIntent = Intent(app, AlarmReceiver::class.java)
private val _timeSelection = MutableLiveData<Int>()
val timeSelection: LiveData<Int>
get() = _timeSelection
private val _elapsedTime = MutableLiveData<Long>()
val elapsedTime: LiveData<Long>
get() = _elapsedTime
private var _alarmOn = MutableLiveData<Boolean>()
val isAlarmOn: LiveData<Boolean>
get() = _alarmOn
private lateinit var timer: CountDownTimer
init {
_alarmOn.value = PendingIntent.getBroadcast(
getApplication(),
REQUEST_CODE,
notifyIntent,
PendingIntent.FLAG_NO_CREATE
) != null
notifyPendingIntent = PendingIntent.getBroadcast(
getApplication(),
REQUEST_CODE,
notifyIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
timerLengthOptions = app.resources.getIntArray(R.array.minutes_array)
//If alarm is not null, resume the timer back for this alarm
if (_alarmOn.value!!) {
createTimer()
}
}
/**
* Turns on or off the alarm
*
* @param isChecked, alarm status to be set.
*/
fun setAlarm(isChecked: Boolean) {
when (isChecked) {
true -> timeSelection.value?.let { startTimer(it) }
false -> cancelNotification()
}
}
/**
* Sets the desired interval for the alarm
*
* @param timerLengthSelection, interval timerLengthSelection value.
*/
fun setTimeSelected(timerLengthSelection: Int) {
_timeSelection.value = timerLengthSelection
}
/**
* Creates a new alarm, notification and timer
*/
private fun startTimer(timerLengthSelection: Int) {
_alarmOn.value?.let {
if (!it) {
_alarmOn.value = true
val selectedInterval = when (timerLengthSelection) {
0 -> second * 10 //For testing only
else ->timerLengthOptions[timerLengthSelection] * minute
}
val triggerTime = SystemClock.elapsedRealtime() + selectedInterval
// Step 1.5 get an instance of NotificationManager and call sendNotification
val notificationManager = ContextCompat.getSystemService(
app,
NotificationManager::class.java
) as NotificationManager
notificationManager.sendNotification(app.getString(R.string.timer_running), app)
// Step 1.15 call cancel notification
AlarmManagerCompat.setExactAndAllowWhileIdle(
alarmManager,
AlarmManager.ELAPSED_REALTIME_WAKEUP,
triggerTime,
notifyPendingIntent
)
viewModelScope.launch {
saveTime(triggerTime)
}
}
}
createTimer()
}
/**
* Creates a new timer
*/
private fun createTimer() {
viewModelScope.launch {
val triggerTime = loadTime()
timer = object : CountDownTimer(triggerTime, second) {
override fun onTick(millisUntilFinished: Long) {
_elapsedTime.value = triggerTime - SystemClock.elapsedRealtime()
if (_elapsedTime.value!! <= 0) {
resetTimer()
}
}
override fun onFinish() {
resetTimer()
}
}
timer.start()
}
}
/**
* Cancels the alarm, notification and resets the timer
*/
private fun cancelNotification() {
resetTimer()
alarmManager.cancel(notifyPendingIntent)
}
/**
* Resets the timer on screen and sets alarm value false
*/
private fun resetTimer() {
timer.cancel()
_elapsedTime.value = 0
_alarmOn.value = false
}
private suspend fun saveTime(triggerTime: Long) =
withContext(Dispatchers.IO) {
prefs.edit().putLong(TRIGGER_TIME, triggerTime).apply()
}
private suspend fun loadTime(): Long =
withContext(Dispatchers.IO) {
prefs.getLong(TRIGGER_TIME, 0)
}
}
通知渠道
-
创建一个扩展 fun createChannel(并在 onCreate 中调用它)
- 创建 NotificationChannel 实例 - 传递 id、名称和重要性
- 在 NotificationChannel obj 上,设置道具 - enableLights、lightColor、enableVibration、通道描述
- 通过调用 getSystemService 获取 NotificationManager 的实例
- 在 NotificationManager 上调用 createNotificationChannel 并传递 NotificationChannel obj。
- 要创建频道,请调用此createChannel分机。乐趣在onCreate乐趣。
- NotificationUtils → build:验证你之前设置的频道id是否正确
- 拉动状态栏,观察通知标题、内容和图标是否与您在前面步骤中设置的一样。
- 要验证新创建的频道,请关闭应用并找到应用图标。长按应用程序图标并选择应用程序信息。
// EggTimerFragment.kt
package com.example.android.eggtimernotifications.ui
import android.app.NotificationChannel
import android.app.NotificationManager
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.example.android.eggtimernotifications.R
import com.example.android.eggtimernotifications.databinding.FragmentEggTimerBinding
import com.google.firebase.messaging.FirebaseMessaging
class EggTimerFragment : Fragment() {
private val TOPIC = "breakfast"
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding: FragmentEggTimerBinding = DataBindingUtil.inflate(
inflater, R.layout.fragment_egg_timer, container, false
)
val viewModel = ViewModelProvider(this).get(EggTimerViewModel::class.java)
binding.eggTimerViewModel = viewModel
binding.lifecycleOwner = this.viewLifecycleOwner
// Step 1.7 call create channel
createChannel(
getString(R.string.egg_notification_channel_id),
getString(R.string.egg_notification_channel_name)
)
return binding.root
}
private fun createChannel(channelId: String, channelName: String) {
// Step 1.6 START create a channel
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannel = NotificationChannel(
channelId,
channelName,
// Change importance
NotificationManager.IMPORTANCE_LOW
)
notificationChannel.enableLights(true)
notificationChannel.lightColor = Color.RED
notificationChannel.enableVibration(true)
notificationChannel.description = "Time for breakfast!"
val notificationManager = requireActivity().getSystemService(
NotificationManager::class.java
)
notificationManager.createNotificationChannel(notificationChannel)
} else {
Log.d(TAG, "VERSION.SDK_INT < O")
Toast.makeText(context, "VERSION.SDK_INT < O", Toast.LENGTH_SHORT).show()
}
// Step 1.6 END create a channel
}
companion object {
private const val TAG = "EggTimerFragment"
fun newInstance() = EggTimerFragment()
}
}
添加新通知
报警接收器
向您的应用添加通知
-
使用 AlarmReceiver 创建另一个通知
- 创建一个继承 BroadcastReceiver 的 AlarmReceiver 类
- 通过调用 getSystemService 获取 NotificationManager 的实例并在其上 调用sendNotification
package com.example.android.eggtimernotifications.receiver
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.widget.Toast
import androidx.core.content.ContextCompat
import com.example.android.eggtimernotifications.R
import com.example.android.eggtimernotifications.util.sendNotification
class AlarmReceiver: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// Step 1.10 remove toast (since we're now sending a notification for the same)
// Toast.makeText(context, context.getText(R.string.eggs_ready), Toast.LENGTH_SHORT).show()
// TODO: Step 1.9 add call to sendNotification
val notificationManager = ContextCompat.getSystemService(
context,
NotificationManager::class.java
) as NotificationManager
notificationManager.sendNotification(
context.getText(R.string.eggs_ready).toString(),
context
)
}
}
使用 PendingIntent 从通知返回到应用程序
通知工具
添加内容意图
-
点击通知导航回应用程序
- 创建意图
- 创建待处理的意图
- 通过在 NotificationBuilder 上调用 setContentIntent 将待处理的意图传递给通知
- 要让通知在您点击它时自行消失,因为它会将您带到应用程序,请将 setAutoCancel 设置为 true
package com.example.android.eggtimernotifications.util import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent import android.graphics.BitmapFactory import androidx.core.app.NotificationCompat import com.example.android.eggtimernotifications.MainActivity import com.example.android.eggtimernotifications.R import com.example.android.eggtimernotifications.receiver.SnoozeReceiver // Notification ID. private val NOTIFICATION_ID = 0 private val REQUEST_CODE = 0 private val FLAGS = 0 // Step 1.1 extension function to send messages (GIVEN) /** * Builds and delivers the notification. * * @param context, activity context. */ fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) { // Create the content intent for the notification, which launches // this activity // Step 1.11 create intent val contentIntent = Intent(applicationContext, MainActivity::class.java) // Step 1.12 create PendingIntent val contentPendingIntent = PendingIntent.getActivity( applicationContext, NOTIFICATION_ID, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT ) // TODO: Step 2.0 add style // TODO: Step 2.2 add snooze action // Step 1.2 get an instance of NotificationCompat.Builder // Build the notification val builder = NotificationCompat.Builder( applicationContext, applicationContext.getString(R.string.egg_notification_channel_id) ) // TODO: Step 1.8 use the new 'breakfast' notification channel // Step 1.3 set title, text and icon to builder .setSmallIcon(R.drawable.cooked_egg) .setContentTitle(applicationContext .getString(R.string.notification_title)) .setContentText(messageBody) // Step 1.13 set content intent .setContentIntent(contentPendingIntent) .setAutoCancel(true) // TODO: Step 2.1 add style to builder // TODO: Step 2.3 add snooze action // TODO: Step 2.5 set priority // Step 1.4 call notify notify(NOTIFICATION_ID, builder.build()) } // TODO: Step 1.14 Cancel all notifications
使用 cancelAll 清除以前的通知
NotificationUtils, EggTimerViewModel
取消通知
-
创建一个扩展乐趣 cancelNotifications
- 在 NotificationManager 上添加一个调用 cancelAll 的扩展 fun
通知工具
funNotificationManager.cancelNotifications() { cancelAll() }
-
在 startTimer fun 中调用扩展 fun cancelNotifications
EggTimerViewModel
notificationManager.cancelNotifications()
样式化您的通知
通知工具
-
在 BigPictureStyle 中设置通知样式
- 使用 BitmapFactory 从资源中加载图像
- 创建一个新的 BigPictureStyle bigPicStyle并设置您的图像
- 要在通知展开时让大图标消失,请将 bigLargeIcon 设置为 null
- 将新样式bigPicStyle设置为 NotificationBuilder
- 要在通知折叠时将图像显示为较小的图标,请在构建器上将图像设置为 setLargeIcon
package com.example.android.eggtimernotifications.util import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent import android.graphics.BitmapFactory import androidx.core.app.NotificationCompat import com.example.android.eggtimernotifications.MainActivity import com.example.android.eggtimernotifications.R import com.example.android.eggtimernotifications.receiver.SnoozeReceiver // Notification ID. private const val NOTIFICATION_ID = 0 private const val REQUEST_CODE = 0 private const val FLAGS = 0 // Step 1.1 extension function to send messages (GIVEN) /** * Builds and delivers the notification. * * @param context, activity context. */ fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) { // Create the content intent for the notification, which launches // this activity // Step 1.11 create intent val contentIntent = Intent(applicationContext, MainActivity::class.java) // Step 1.12 create PendingIntent val contentPendingIntent = PendingIntent.getActivity( applicationContext, NOTIFICATION_ID, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT ) // Step 2.0 add style val eggImage = BitmapFactory.decodeResource( applicationContext.resources, R.drawable.cooked_egg ) val bigPicStyle = NotificationCompat.BigPictureStyle() .bigPicture(eggImage) .bigLargeIcon(null) // TODO: Step 2.2 add snooze action // Step 1.2 get an instance of NotificationCompat.Builder // Build the notification val builder = NotificationCompat.Builder( applicationContext, applicationContext.getString(R.string.egg_notification_channel_id) ) // TODO: Step 1.8 use the new 'breakfast' notification channel // Step 1.3 set title, text and icon to builder .setSmallIcon(R.drawable.cooked_egg) .setContentTitle(applicationContext .getString(R.string.notification_title)) .setContentText(messageBody) // Step 1.13 set content intent .setContentIntent(contentPendingIntent) .setAutoCancel(true) // Step 2.1 add style to builder .setStyle(bigPicStyle) .setLargeIcon(eggImage) // TODO: Step 2.3 add snooze action // TODO: Step 2.5 set priority // TODO: Step 1.4 call notify notify(NOTIFICATION_ID, builder.build()) } // Step 1.14 Cancel all notifications fun NotificationManager.cancelNotifications() { cancelAll() }
添加贪睡动作
贪睡接收器,NotificationUtils
-
安排新通知并删除已延后的通知
- 获取 NotificationManager 的实例
package com.example.android.eggtimernotifications.receiver import android.app.AlarmManager 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.text.format.DateUtils import androidx.core.app.AlarmManagerCompat import androidx.core.content.ContextCompat class SnoozeReceiver: BroadcastReceiver() { private val REQUEST_CODE = 0 override fun onReceive(context: Context, intent: Intent) { val triggerTime = SystemClock.elapsedRealtime() + DateUtils.MINUTE_IN_MILLIS val notifyIntent = Intent(context, AlarmReceiver::class.java) val notifyPendingIntent = PendingIntent.getBroadcast( context, REQUEST_CODE, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT ) val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager AlarmManagerCompat.setExactAndAllowWhileIdle( alarmManager, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, notifyPendingIntent ) val notificationManager = ContextCompat.getSystemService( context, NotificationManager::class.java ) as NotificationManager notificationManager.cancelAll() } }
-
当用户使用 PendingIntent 轻按贪睡按钮时,设置新警报以在 60 秒后发布新通知
- 创建意图
- 创建一个 PendingIntent
- 在 NotificationBuilder 上调用 addAction fun
package com.example.android.eggtimernotifications.util import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent import android.graphics.BitmapFactory import androidx.core.app.NotificationCompat import com.example.android.eggtimernotifications.MainActivity import com.example.android.eggtimernotifications.R import com.example.android.eggtimernotifications.receiver.SnoozeReceiver // Notification ID. private const val NOTIFICATION_ID = 0 private const val REQUEST_CODE = 0 private const val FLAGS = 0 // Step 1.1 extension function to send messages (GIVEN) /** * Builds and delivers the notification. * * @param context, activity context. */ fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) { // Create the content intent for the notification, which launches // this activity // Step 1.11 create intent val contentIntent = Intent(applicationContext, MainActivity::class.java) // Step 1.12 create PendingIntent val contentPendingIntent = PendingIntent.getActivity( applicationContext, NOTIFICATION_ID, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT ) // Step 2.0 add style val eggImage = BitmapFactory.decodeResource( applicationContext.resources, R.drawable.cooked_egg ) val bigPicStyle = NotificationCompat.BigPictureStyle() .bigPicture(eggImage) .bigLargeIcon(null) // Step 2.2 add snooze action val snoozeIntent = Intent(applicationContext, SnoozeReceiver::class.java) val snoozePendingIntent = PendingIntent.getBroadcast( applicationContext, REQUEST_CODE, snoozeIntent, FLAGS ) // Step 1.2 get an instance of NotificationCompat.Builder // Build the notification val builder = NotificationCompat.Builder( applicationContext, applicationContext.getString(R.string.egg_notification_channel_id) ) // TODO: Step 1.8 use the new 'breakfast' notification channel // Step 1.3 set title, text and icon to builder .setSmallIcon(R.drawable.cooked_egg) .setContentTitle(applicationContext .getString(R.string.notification_title)) .setContentText(messageBody) // Step 1.13 set content intent .setContentIntent(contentPendingIntent) .setAutoCancel(true) // Step 2.1 add style to builder .setStyle(bigPicStyle) .setLargeIcon(eggImage) // Step 2.3 add snooze action .addAction( R.drawable.egg_icon, applicationContext.getString(R.string.snooze), snoozePendingIntent ) // TODO: Step 2.5 set priority // Step 1.4 call notify notify(NOTIFICATION_ID, builder.build()) } // Step 1.14 Cancel all notifications fun NotificationManager.cancelNotifications() { cancelAll() }
改变重要性
EggTimerFragment、NotificationUtils
-
设置图片
// NotificationUtils() // Step 2.0 add style val eggImage = BitmapFactory.decodeResource( applicationContext.resources, R.drawable.cooked_egg ) val bigPicStyle = NotificationCompat.BigPictureStyle() .bigPicture(eggImage) .bigLargeIcon(null) ********************************************************************************** val builder = NotificationCompat.Builder( applicationContext, applicationContext.getString(R.string.egg_notification_channel_id) ) ... // Step 2.1 add style to builder .setStyle(bigPicStyle) .setLargeIcon(eggImage)
-
将重要性从低到高
-
NotificationManager → 重要性
... class EggTimerFragment : Fragment() { ... private fun createChannel(channelId: String, channelName: String) { // Step 1.6 START create a channel if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val notificationChannel = NotificationChannel( channelId, channelName, // Change importance NotificationManager.IMPORTANCE_HIGH ) notificationChannel.enableLights(true) notificationChannel.lightColor = Color.RED notificationChannel.enableVibration(true) notificationChannel.description = "Time for breakfast!" val notificationManager = requireActivity().getSystemService( NotificationManager::class.java ) notificationManager.createNotificationChannel(notificationChannel) } else { Log.d(TAG, "VERSION.SDK_INT < O") Toast.makeText(context, "VERSION.SDK_INT < O", Toast.LENGTH_SHORT).show() } // Step 1.6 END create a channel } ... }
-
-
将 PRIORITY_HIGH 添加到通知构建器对象
要支持运行 API 级别 25 或更低级别的设备,您必须使用 NotificationCompat 类中的常量为每个通知调用 setPriority。为了解决这个问题,我们正在执行上述操作。
// *NotificationUtils.kt* fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) { ... // Step 1.2 get an instance of NotificationCompat.Builder // Build the notification val builder = NotificationCompat.Builder( applicationContext, applicationContext.getString(R.string.egg_notification_channel_id) ) ... // Step 2.5 set priority .setPriority(NotificationCompat.PRIORITY_HIGH) ... } ...
通知徽章
EggTimerFragment
-
禁用徽章
- 在 NotificationChannel obj 上将 setShowBadge 设置为 false。
// *EggTimerFragment.kt* private fun createChannel(channelId: String, channelName: String) { // Step 1.6 START create a channel if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val notificationChannel = NotificationChannel( channelId, channelName, // Change importance NotificationManager.IMPORTANCE_HIGH ) // Step 2.6 Disable badges for this channel .apply { setShowBadge(false) } ... } }
链接:https://dev.to/apigeoneer/creating-notifications-in-android-with-kotlin-56fi