인프런 커뮤니티 질문&답변

khs50851님의 프로필 이미지
khs50851

작성한 질문수

[초중급편] 안드로이드 데이팅 앱 만들기(Android Kotlin)

매칭이 되면 Notification 띄우기

Notification 구현시 컴파일에러

작성

·

250

0

class MainActivity : AppCompatActivity() {

    lateinit var cardStackAdapter: CardStackAdapter
    lateinit var manager : CardStackLayoutManager
    private val TAG = "MainActivity"
    private val usersDataList = mutableListOf<UserDataModel>()
    private var userCount = 0
    private val uid =FireBaseAuthUtils.getUid()
    private lateinit var currentUserGender : String
    private val REQUEST_CODE = 123

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val isTiramisuOrHigher = Build.VERSION.SDK_INT>= Build.VERSION_CODES.TIRAMISU
        val notificationPermission = Manifest.permission.POST_NOTIFICATIONS

        var hasNotificationPermission =
            if (isTiramisuOrHigher)
                ContextCompat.checkSelfPermission(this,notificationPermission) == PackageManager.PERMISSION_GRANTED
            else true

        val launcher = registerForActivityResult(ActivityResultContracts.RequestPermission()){
            hasNotificationPermission = it
        }

        if(!hasNotificationPermission) {
            launcher.launch(notificationPermission)
        }

        val setting = findViewById<ImageView>(R.id.settingIcon)
        setting.setOnClickListener{
//            val auth = Firebase.auth
//            auth.signOut()
//
            val intent = Intent(this,SettingActivity::class.java)
            startActivity(intent)
        }

        val cardStackView = findViewById<CardStackView>(R.id.cardStackView)

        manager = CardStackLayoutManager(baseContext,object : CardStackListener{
            override fun onCardDragging(direction: Direction?, ratio: Float) {

            }

            override fun onCardSwiped(direction: Direction?) {
                if(direction == Direction.Right){
                    userLikeOtherUser(uid,usersDataList[userCount].uid.toString())
                }

                if(direction == Direction.Left){

                }

                userCount = userCount + 1

                if(userCount == usersDataList.count()){
                    getUserDataList(currentUserGender)
                    Toast.makeText(this@MainActivity,"유저 리스트를 새롭게 받아옵니다.",Toast.LENGTH_SHORT).show()
                }

            }

            override fun onCardRewound() {

            }

            override fun onCardCanceled() {

            }

            override fun onCardAppeared(view: View?, position: Int) {

            }

            override fun onCardDisappeared(view: View?, position: Int) {

            }

        })

        cardStackAdapter = CardStackAdapter(baseContext,usersDataList)
        cardStackView.layoutManager = manager
        cardStackView.adapter = cardStackAdapter

//        getUserDataList()
        getMyUserData()
    }

    private fun getMyUserData(){
        val postListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {

                val data = dataSnapshot.getValue(UserDataModel::class.java)
                Log.d(TAG,data.toString())

                currentUserGender = data?.gender.toString()

                getUserDataList(currentUserGender)

            }

            override fun onCancelled(databaseError: DatabaseError) {
                // Getting Post failed, log a message
                Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
            }
        }
        FireBaseRef.userInfoRef.child(uid).addValueEventListener(postListener)
    }

    private fun getUserDataList(currentUserGender : String){
        val postListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {

//                val post = dataSnapshot.getValue<Post>()

                for(dataModel in dataSnapshot.children){
                    val user = dataModel.getValue(UserDataModel::class.java)
                    if(!user!!.gender.toString().equals(currentUserGender)){
                        usersDataList.add(user!!)
                    }

                }
                cardStackAdapter.notifyDataSetChanged()
            }

            override fun onCancelled(databaseError: DatabaseError) {
                // Getting Post failed, log a message
                Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
            }
        }
        FireBaseRef.userInfoRef.addValueEventListener(postListener)
    }

    private fun userLikeOtherUser(myUid : String, otherUid : String) {
        FireBaseRef.userLikeRef.child(myUid).child(otherUid).setValue("true")
        getOtherUserLikeList(otherUid)
    }

    private fun getOtherUserLikeList(otherUid : String){
        val postListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {

                // 여기 리스트에서 나의 UID가 있나 확인 필요
                for(dataModel in dataSnapshot.children){

                    val likeUserKey = dataModel.key.toString()

                    if(likeUserKey.equals(uid)){
                        Toast.makeText(this@MainActivity,"매칭완료",Toast.LENGTH_SHORT).show()
                        createNotificationChannel()
                        sendNotification()

                    }

                }

            }

            override fun onCancelled(databaseError: DatabaseError) {
                // Getting Post failed, log a message
                Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
            }
        }
        FireBaseRef.userLikeRef.child(otherUid).addValueEventListener(postListener)
    }

    // 알림

    private fun createNotificationChannel() {
        // Create the NotificationChannel, but only on API 26+ because
        // the NotificationChannel class is not in the Support Library.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // 채널 이름
            val name = "name"
            val descriptionText = "description"
            val importance = NotificationManager.IMPORTANCE_DEFAULT
            val channel = NotificationChannel("TestChannel", name, importance).apply {
                description = descriptionText
            }
            // Register the channel with the system.
            val notificationManager: NotificationManager =
                getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(channel)
        }
    }

    private fun sendNotification(){

        var builder = NotificationCompat.Builder(this, "TestChannel")
            .setSmallIcon(R.drawable.ic_launcher_background)
            .setContentTitle("My notification")
            .setContentText("Hello World!")
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .setCategory(NotificationCompat.CATEGORY_MESSAGE)
        with(NotificationManagerCompat.from(this)){
            notify(123,builder.build())
        }

    }

}

안녕하세요. 알림기능 구현하는데

notify(123,builder.build())

위 부분에서 Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with checkPermission) or explicitly handle a potential SecurityException 이런 에러가 아직도 발생하고있는데 어떻게 해결할수 있을지 자세한 답변을 부탁드려도될까요? 구글링 해봤는데 명확히 어떤코드 입력해서 컴파일에러를 지울 수 있을지 모르겠습니다. 빠른답변 부탁드리겠습니다.

답변 2

0

khs50851님의 프로필 이미지
khs50851
질문자

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    if (ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.POST_NOTIFICATIONS
        ) == PackageManager.PERMISSION_GRANTED
    ) {
        var builder = NotificationCompat.Builder(this, "TestChannel")
            .setSmallIcon(R.drawable.ic_launcher_background)
            .setContentTitle("매칭완료")
            .setContentText("매칭이 성사되었습니다. 메세지를 확인하세요!")
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .setCategory(NotificationCompat.CATEGORY_MESSAGE)
        with(NotificationManagerCompat.from(this)) {
            notify(123, builder.build())
        }
    } else {
        // 권한 요청
        requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), REQUEST_CODE)
    }
}

위와 같이 코드를 작성하라는 말씀이신가요? 이렇게하면 컴파일 에러는 사라지는데 서로 좋아요시 알림이 오지 않네요..

AndroidManifest.xml파일엔

<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

위 코드가 추가되어있습니다.

조금 더 검색해봤는데 아래 with~ 코드를 지우고

with(NotificationManagerCompat.from(this)){
    notify(123,builder.build())
}
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(REQUEST_CODE, builder.build())

이렇게 코드를 넣으니 컴파일 에러 없이 실행 되네요 REQUEST_CODE엔 대충 정수 123을 넣었습니다.

개복치개발자님의 프로필 이미지
개복치개발자
지식공유자

전체 코드를 압축해서 구글 드라이브에 업로드 후 링크를 공유해주시겠어요?
한번 살펴보겠습니다!

0

안녕하세요, 인프런 AI 인턴입니다. khs50851님의 질문에 대해 관련 자료를 찾았습니다.

질문하신 내용에서 Android Tiramisu 버전(API level 33 이상)에서는 Notification을 사용하기 위해 사용자의 명시적인 권한 요청이 필요합니다. 이를 위해 Manifest.permission.POST_NOTIFICATIONS 권한이 필수적인데, 질문하신 부분을 해결하기 위해서는 사전에 이 권한을 확인하고 요청하는 과정이 필요합니다.

“Notification 띄우기를 하는 중에 났던 에러들 입니다!” 이 게시물은 Notification 관련 이슈들에 대해서 정리하고 있으며, 질문자님의 문제와 관련된 내용도 포함되어 있습니다. 특히, 해당 게시물에서는 API level 33 이상에서의 Notification 사용 방법 및 권한 요청 과정에 대하여 설명하고 있습니다.

이 게시글에서는 아래처럼 권한을 요청하는 코드 예시를 제공하고 있습니다:

// API level 33 이상일 경우
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    if (ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.POST_NOTIFICATIONS
        ) == PackageManager.PERMISSION_GRANTED
    ) {
        // 권한이 있을 때의 알림 전송 로직
    } else {
        // 권한 요청
        requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), REQUEST_CODE)
    }
} else {
    // API level 33 미만일 경우, 권한 없이 알림 전송 로직
}

위 예시와 같이, 알림을 보내기 전에 사용자로부터 필요한 권한을 명시적으로 요청하는 로직이 필요합니다. 이 과정을 거친 후에 notify 메서드를 호출하면, 권한 관련 에러 없이 알림을 전송할 수 있습니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

khs50851님의 프로필 이미지
khs50851

작성한 질문수

질문하기