코딩하는 개굴이

[안드로이드] Local Notification 매일 특정 시간에 보내기 본문

안드로이드

[안드로이드] Local Notification 매일 특정 시간에 보내기

개굴이모자 2022. 2. 27. 01:16
반응형

[안드로이드] Local Notification 매일 특정 시간에 보내기

Gradle 설정

dependencies {
    //androidx
    implementation 'androidx.core:core-ktx:1.7.0' // 노티피케이션을 위한 패키지
    ....
}

Manifest 설정

Manifest에 AlarmReceiver 를 등록한다.

안드로이드는 부팅이 끝나면 'BOOT_COMPLETED' 인 intent를 브로드캐스트 한다.

PendingIntent를 이용해 Notification을 사용할 경우, 재부팅 후에는 해당 설정이 증발한다. 따라서, 재부팅 후에도 PendingIntent 가 남아있도록 하려면, Manifest에 부팅 시, BroadCast 를 실행하도록 BOOT_COMPLETED 설정이 필요하다.

<application
        android:name=".SampleApplication"
        ...
        >
        ...
        <receiver android:name=".AlarmReceiver"
            android:enabled="true"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
    </application>

Application

Application 에 alarmManager 를 등록하였지만, MainActivity 등, 실제 사용하는 곳에서 선언하여도 무방하다.

internal var alarmManager: AlarmManager? = null

class SampleApplication : Application() {
    ...

MainActivity

MainActivity 에서 alarmManager를 할당한다.

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager?
    }

AlarmReceiver

Oreo 이후에는 NotificationChannel 을 생성해야한다.

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.Build
import androidx.core.app.NotificationCompat
import com.sample.sampleapp.presentation.MainActivity


class AlarmReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        createNotificationChannel(context)
        deliverNotification(context)
    }

    private fun createNotificationChannel(context: Context){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notificationChannel = NotificationChannel(
                CHANNEL_ID, // 채널의 아이디
                CHANNEL_NAME, // 채널의 이름
                NotificationManager.IMPORTANCE_HIGH
                /*
                1. IMPORTANCE_HIGH = 알림음이 울리고 헤드업 알림으로 표시
                2. IMPORTANCE_DEFAULT = 알림음 울림
                3. IMPORTANCE_LOW = 알림음 없음
                4. IMPORTANCE_MIN = 알림음 없고 상태줄 표시 X
                 */
            )
            notificationChannel.enableLights(true) // 불빛
            notificationChannel.lightColor = R.color.accentNormal // 색상
            notificationChannel.enableVibration(true) // 진동 여부
            notificationChannel.description = context.getString(R.string.app_name) // 채널 정보
            notificationManager?.createNotificationChannel(
                notificationChannel)
        }
    }

    private fun deliverNotification(context: Context){
        val contentIntent = Intent(context, MainActivity::class.java)
        val contentPendingIntent = PendingIntent.getActivity(
            context,
            NOTIFICATION_ID, // requestCode
            contentIntent, // 알림 클릭 시 이동할 인텐트
            PendingIntent.FLAG_UPDATE_CURRENT
            /*
            1. FLAG_UPDATE_CURRENT : 현재 PendingIntent를 유지하고, 대신 인텐트의 extra data는 새로 전달된 Intent로 교체
            2. FLAG_CANCEL_CURRENT : 현재 인텐트가 이미 등록되어있다면 삭제, 다시 등록
            3. FLAG_NO_CREATE : 이미 등록된 인텐트가 있다면, null
            4. FLAG_ONE_SHOT : 한번 사용되면, 그 다음에 다시 사용하지 않음
             */
        )

        val builder = NotificationCompat.Builder(context, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground) // 아이콘
            .setContentTitle("알람이개굴") // 제목
            .setContentText("알람 내용이개굴") // 내용
            .setContentIntent(contentPendingIntent)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setAutoCancel(true)
            .setDefaults(NotificationCompat.DEFAULT_ALL)

        notificationManager?.notify(NOTIFICATION_ID, builder.build())
    }

    companion object {
        private const val NOTIFICATION_ID = 0
        private const val CHANNEL_ID = "channel_id"
        private const val CHANNEL_NAME = "ChannelName"
    }
}

Set/Cancel Alarm

Cancel 할 경우와 Set 할 경우에 해당한다.
매일 일정 시각에 알람을 반복을 위해서는 alarmManager.setRepeating에서 AlarmManager.INTERVAL_DAY를 설정한다.

private fun cancelAlarm() {
        val receiverIntent = Intent(getApplication<Application>(), AlarmReceiver::class.java)
        val pendingIntent = PendingIntent.getBroadcast(getApplication<Application>(), 0, receiverIntent, 0)
        alarmManager?.cancel(pendingIntent)
    }

    private fun setAlarm(hour: Int, minute: Int) {
        //옵션값에 따라서, 푸시 설정이 되지 않을 수 있도록 함
        if (!pushAvailable.value) return 

        //AlarmReceiver에 값 전달
        val receiverIntent = Intent(getApplication<Application>(), AlarmReceiver::class.java)
        val pendingIntent = PendingIntent.getBroadcast(getApplication<Application>(), 0, receiverIntent, 0)

        //alarm 등록 전, 이전 push cancel
        alarmManager?.cancel(pendingIntent)

        // Set the alarm to start at time and minute
        val calendar: Calendar = Calendar.getInstance().apply {
            timeInMillis = System.currentTimeMillis()
            set(Calendar.HOUR_OF_DAY, hour)
            set(Calendar.MINUTE, minute)
            set(Calendar.SECOND, 0)
        }

        if (calendar.time < Date()) { //설정한 시간에 따라, 알람이 설정이 안될 수 있으므로 달 첫번째 부터의 시간을 설정
            calendar.add(Calendar.DAY_OF_MONTH, 1)
        }

        alarmManager?.setRepeating(
            AlarmManager.RTC_WAKEUP,
            calendar.timeInMillis,
            AlarmManager.INTERVAL_DAY,
            pendingIntent
        )
    }

참고 링크

반응형
Comments