오늘은 안드로이드 스튜디오에서 알람을 띄우도록 하는 방법에 대해 공부해 보았습니다.
Notification을 통해 앱 사용자에게 알람을 줄 수 있도록 만들 수 있습니다.
Notification에 다양한 기능을 줄 수 있겠지만 지금은 간단하게 앱의 이미지와 제목, 내용만 넣어 간단한 알람을 만들어 보겠습니다.
먼저 AndroidManifest에 receiver을 추가하겠습니다.
application 태그 안에 receiver을 추가하도록 하겠습니다.
<!--알람 발생 권한-->
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<application
...
<receiver
android:name=".AlertReceiver"
android:enabled="true"
android:exported="false"
/>
...
</application>
그 후 AlertReceiver를 생성하여 줍니다. (new -> kotlin class -> name설정은 자유입니다. 저는 AlertReceiver로 하였습니다.)
class AlertReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
var notificationHelper : NotificationHelper = NotificationHelper(context)
var nb : NotificationCompat.Builder = notificationHelper.getChannelNotification()
//알림 호출 코드
notificationHelper.getManager().notify(1, nb.build())
}
}
예를 들어 Android 시스템은 시스템 부팅 또는 기기 충전 시작과 같은 다양한 시스템 이벤트가 발생할 때 브로드캐스트를 전송합니다. 또한 앱은 맞춤 브로드캐스트를 전송하여 다른 앱이 관심을 가질만한 사항(예: 일부 새로운 데이터가 다운로드됨)을 관련 앱에 알릴 수 있습니다.(Broadcasts)
class NotificationHelper(context : Context?) : ContextWrapper(context) {
private val channelID = "channelID"
private val channelName = "channelName"
init {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createChannel()
}
}
private fun createChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
var channel = NotificationChannel(channelID, channelName,
NotificationManager.IMPORTANCE_DEFAULT)
//이 채널에 게시된 알림이 해당 기능을 지원하는 장치에서 알림 표시등을 표시할지 여부를 설정합니다.
channel.enableLights(true)
//이 채널에 게시된 알림이 해당 기능을 지원하는 장치에서 진동 등을 표시할지 여부를 설정합니다.
channel.enableVibration(true)
//이 채널에 게시된 알림에 대한 알림 표시등 색상을 설정
channel.lightColor = Color.GREEN
//이 채널에 게시된 알림이 전체 또는 수정된 형태로 잠금 화면에 표시되는지 여부를 설정
channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
getManager().createNotificationChannel(channel)
}
}
fun getManager() : NotificationManager {
return getSystemService(NOTIFICATION_SERVICE) as NotificationManager
}
//notification 설정
fun getChannelNotification() : NotificationCompat.Builder {
return NotificationCompat.Builder(applicationContext, channelID)
.setContentTitle("제목")
.setContentText("내용")
.setSmallIcon(R.drawable.ic_launcher_background)
}
}
안드로이드 버전에 따른 생성방법이 다릅니다. 현재는 버전이 오레오거나 그 이상이므로 채널을 생성해주어야 합니다.
createChannel로 채널을 생성해 주겠습니다. 위 코드에 주석을 참고해서 생성하였습니다.
getManager는 NotificationManager 생성 함수입니다.
getChannelNotification은 실제로 표시될 알람의 설정값입니다.
다음으로 실제로 시간을 등록해 보도록 하겠습니다.
저는 시간에 따른 알람을 만들어 줄 것이기 때문에 코드를 작성할 xml에서 spinner형태로 TimePicker를 생성하여줍니다. TimePicker에는 clock, spinner 중에서 원하는 걸로 고르거나 다양한 커스텀을 통해 만들어 보시길 바랍니다. 저는 아래와 같이 생성하였습니다.
<!--작성자의 xml-->
<TimePicker
android:id="@+id/timePicker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="122dp"
android:timePickerMode="spinner"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
private lateinit var setAlarmTime : Calendar
...
//onCreate
timepicker.setOnTimeChangedListener(OnTimeChangedListener { view, hourOfDay, minute ->
var hour = hourOfDay
setAlarmTime = Calendar.getInstance()
//오전 오후를 표시하기 위해 사용된 코드
if (hour > 12) {
hour -= 12
scheduleEditBinding.tvTime.text = "오후 $hour 시 $minute 분"
targetTime = "오후 $hour 시 $minute 분"
//시간 설정
setAlarmTime.set(Calendar.HOUR_OF_DAY, hourOfDay)
setAlarmTime.set(Calendar.MINUTE, minute)
setAlarmTime.set(Calendar.SECOND, 0)
} else {
scheduleEditBinding.tvTime.text = "오전 $hour 시 $minute 분"
targetTime = "오전 $hour 시 $minute 분"
}
})
scheduleEditBinding.scheduleSaveBtn.setOnClickListener {
val contentPost = scheduleEditBinding.scheduleText.text.toString() //schedule edit text
//알람 설정
startAlarm(setAlarmTime, contentPost)
}
//알림 설정
private fun startAlarm(c : Calendar, content : String?) {
var alarmManager : AlarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
var curTime = DateFormat.getTimeInstance(DateFormat.SHORT).format(c.time)
var bundle = Bundle()
bundle.putString("time", curTime)
bundle.putString("content", content)
var intent = Intent(this, AlertReceiver::class.java).apply {
putExtra("bundle",bundle)
}
//intent를 당장 수행하지 않고 특정시점에 수행하도록 미룰 수 있는 intent
var pendingIntent = PendingIntent.getBroadcast(this, 1, intent, 0)
//설정한 시간이 현재 시간 전이라면 날짜에 +1
if (c.before(Calendar.getInstance())) {
c.add(Calendar.DATE, 1)
}
//setExactAndAllowWhileIdle:절전모드에서도 사용가능
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, setAlarmTime.timeInMillis, pendingIntent)
}
AlarmManager
setAlarmTime에 사용자가 설정한 시간을 세팅하고 사용자가 save버튼을 클릭 하면 알람을 설정합니다.
startAlarm은 content와 시간을 매개변수로 받아 설정하고 싶은 내용과 사용자가 설정한 시간을 토대로 Bundle에 값을 저장 후 intent로 값을 넘겨줄 것입니다.
여기서 Bundle인 이유는? PendingIntent에서 사람들이 저지르는 일반적인 실수는 매번 다른 PendingIntent를 기대하면서 "추가" 콘텐츠만 달라지는 Intent로 여러 PendingIntent 개체를 만드는 것입니다. 이것은 일어나지 않습니다. 그렇기에 intent로 putextra를 통해 key,value를 AlertReceiver로 넘겨주더라도 null이 발생합니다.
class AlertReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
var notificationHelper : NotificationHelper = NotificationHelper(context)
var bundle = intent?.getBundleExtra("bundle")
var time = bundle?.getString("time")
var content = bundle?.getString("content")
var nb : NotificationCompat.Builder = notificationHelper.getChannelNotification(time, content)
//알림 호출 코드
notificationHelper.getManager().notify(1, nb.build())
}
}
bundle로 가져와서 getChannelNotification의 매개변수로 넣고 title과 text를 매개변수를 통해 다시 설정해줍니다.
//notification 설정
fun getChannelNotification(time : String?, content : String?) : NotificationCompat.Builder {
return NotificationCompat.Builder(applicationContext, channelID)
.setContentTitle(time)
.setContentText(content)
.setSmallIcon(R.drawable.ic_launcher_background)
}
만약 안될 경우 - 현재시간과 에뮬레이터의 시간이 맞지 않는 경우가 있습니다. 에뮬레이터 시간을 대한민국 서울로 설정해줍니다. 시간 설정