💻 개발/Android

[Android] 알림 클릭시 Activity, Fragment로 이동

고도고도 2022. 6. 8. 13:05

구현 문제

알림 클릭하면 특정 화면을 띄어줘야 하는 문제였다.

해결 방법

PendingIntent 를 사용하면 된다. Notification 속성에 Intent 속성을 부여할 수 있는데 이 때 PendingIntent 를 넘겨준다. 또한 PendingIntent 객체에 putExtra 로 화면 이동으로 띄어줄 Fragment 의 식별자를 넘겨준다. 이를 활용하여 MainActivity 가 onCreate 됐을 때 getStringExtra 로 Fragment 의 식별자를 가져오고 해당 Fragment 로 전환한다.

코드

- ReceiverService

우선 지난번에 구현했던 이벤트 수신 알림에 대한 코드를 가져왔다.

// 이벤트를 수신했을 경우 사용자에게 알림 전송
    private fun occurEventNotification() {
        val bitmap = BitmapFactory.decodeStream(URL(IMAGE_URL).openConnection().getInputStream())
        
        val intent = Intent(this@ReceiverService, MainActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
            putExtra("Fragment", "eventDetailFragment")
        }

        val pendingIntent: PendingIntent =
            PendingIntent.getActivity(this@ReceiverService, 0, intent, 0)

        val builder = NotificationCompat.Builder(this@ReceiverService, NOTIFICATION_CHANNEL[1])
            .setSmallIcon(R.drawable.ic_baseline_poop_solid_icon)
            .setContentTitle("배변 이벤트 수신")
            .setContentText("Device 1에서 배변 이벤트가 수신되었습니다.")
            .setLargeIcon(bitmap)
            .setStyle(
                NotificationCompat.BigPictureStyle()
                    .bigPicture(bitmap)
                    .bigLargeIcon(null)
            )
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setContentIntent(pendingIntent)
            .setAutoCancel(true)

        // Android 26 이상부터는 NotificationChannel 등록 필요
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel =
                NotificationChannel(
                    NOTIFICATION_CHANNEL[1],
                    "이벤트 발생",
                    NotificationManager.IMPORTANCE_HIGH
                )
            val manager =
                applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            manager.createNotificationChannel(channel)
        }
    }

기존에 Intent 를 사용하던 방식과 같이 Context 와 화면 이동을 할 Class 를 인자로 넣어준다. putExtra 를 통해서 화면 이동을 할 Fragment 의 식별자를 넣어준다. Fragment 가 EventDetailFragment 이기에 eventDetailFragment 로 넣어줬다. 이후 PendingIntent 에 정의한 Intent 를 넘겨주면 된다.

그렇다면 그냥 Intent 를 사용하면 안되는 것일까? 둘의 차이점을 살펴보면 우리가 평소에 사용하던 Intent 는 하나의 앱(프로세스) 내부에서 화면 이동을 하는 것이였지만 PendingIntent 는 하나의 앱에서 다른 앱으로 화면 이동을 하고 있다. NotificationManager 가 Intent 를 실행하여 마치 하나의 앱에서 화면 이동을 실행하는 것처럼 사용하게 한다. 즉 다른 프로세스(안드로이드 시스템) 에서 Intent 를 진행하므로 PendingIntent 를 사용해야 한다. Notification 외에도 앱 외부에서 앱의 특정 화면을 호출해야하는 경우에 PendingIntent 를 사용할 수 있다. (AlarmManager, Widget)

 

- MainActivity

이렇게 PendingIntent 로 MainActivity 를 호출하고 MainActivity 가 onCreate 되면 어떤 Fragment 로 이동할지를 결정한다.

        override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val targetFragment = intent.getStringExtra("Fragment")
        if (targetFragment != null) {
            if (targetFragment == "eventDetailFragment") {
                saveAndChangeFragment(EventDetailFragment())
            } else {
                saveAndChangeFragment(DeviceDetailFragment())
            }
        }


saveAndChangeFragment 메소드는 FrameLayout 에 띄어줄 Fragment 를 지정한다.

 	fun saveAndChangeFragment(fragment: Fragment) {
        supportFragmentManager.beginTransaction()
            .replace(binding.fragmentContainer.id, fragment)
            .addToBackStack(null)
            .commit()
    }

결과

알림 종류에 따라 다른 Fragment 로 이동

학습용 이미지 선택 알림을 클릭 했을 경우에 소켓 재연결이 진행되는데 Intent Flag로 NEW_TASK 와 CLEAR_TASK 를 넘겨줬기 때문이다.

느낀 점